Merge branch 'master' into offmed-staging
This commit is contained in:
@@ -7,6 +7,18 @@ public sealed class EmpSystem : SharedEmpSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<EmpDisabledComponent, ComponentStartup>(OnStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartup(Entity<EmpDisabledComponent> ent, ref ComponentStartup args)
|
||||||
|
{
|
||||||
|
// EmpPulseEvent.Affected will spawn the first visual effect directly when the emp is used
|
||||||
|
ent.Comp.TargetTime = Timing.CurTime + _random.NextFloat(0.8f, 1.2f) * ent.Comp.EffectCooldown;
|
||||||
|
}
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -16,7 +28,7 @@ public sealed class EmpSystem : SharedEmpSystem
|
|||||||
{
|
{
|
||||||
if (Timing.CurTime > comp.TargetTime)
|
if (Timing.CurTime > comp.TargetTime)
|
||||||
{
|
{
|
||||||
comp.TargetTime = Timing.CurTime + _random.NextFloat(0.8f, 1.2f) * TimeSpan.FromSeconds(comp.EffectCooldown);
|
comp.TargetTime = Timing.CurTime + _random.NextFloat(0.8f, 1.2f) * comp.EffectCooldown;
|
||||||
Spawn(EmpDisabledEffectPrototype, transform.Coordinates);
|
Spawn(EmpDisabledEffectPrototype, transform.Coordinates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||||
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
|
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
|
||||||
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
|
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
|
||||||
|
human.AddFunction(ContentKeyFunctions.SmartEquipPocket1);
|
||||||
|
human.AddFunction(ContentKeyFunctions.SmartEquipPocket2);
|
||||||
|
human.AddFunction(ContentKeyFunctions.SmartEquipSuitStorage);
|
||||||
human.AddFunction(ContentKeyFunctions.OpenBackpack);
|
human.AddFunction(ContentKeyFunctions.OpenBackpack);
|
||||||
human.AddFunction(ContentKeyFunctions.OpenBelt);
|
human.AddFunction(ContentKeyFunctions.OpenBelt);
|
||||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||||
|
|||||||
@@ -190,6 +190,9 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
AddHeader("ui-options-header-interaction-adv");
|
AddHeader("ui-options-header-interaction-adv");
|
||||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||||
AddButton(ContentKeyFunctions.SmartEquipBelt);
|
AddButton(ContentKeyFunctions.SmartEquipBelt);
|
||||||
|
AddButton(ContentKeyFunctions.SmartEquipPocket1);
|
||||||
|
AddButton(ContentKeyFunctions.SmartEquipPocket2);
|
||||||
|
AddButton(ContentKeyFunctions.SmartEquipSuitStorage);
|
||||||
AddButton(ContentKeyFunctions.OpenBackpack);
|
AddButton(ContentKeyFunctions.OpenBackpack);
|
||||||
AddButton(ContentKeyFunctions.OpenBelt);
|
AddButton(ContentKeyFunctions.OpenBelt);
|
||||||
AddButton(ContentKeyFunctions.ThrowItemInHand);
|
AddButton(ContentKeyFunctions.ThrowItemInHand);
|
||||||
|
|||||||
5
Content.Client/Power/EntitySystems/BatterySystem.cs
Normal file
5
Content.Client/Power/EntitySystems/BatterySystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Client.Power.EntitySystems;
|
||||||
|
|
||||||
|
public sealed class BatterySystem : SharedBatterySystem;
|
||||||
5
Content.Client/Power/EntitySystems/ChargerSystem.cs
Normal file
5
Content.Client/Power/EntitySystems/ChargerSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Client.Power.EntitySystems;
|
||||||
|
|
||||||
|
public sealed class ChargerSystem : SharedChargerSystem;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.SurveillanceCamera;
|
||||||
|
|
||||||
|
namespace Content.Client.SurveillanceCamera;
|
||||||
|
|
||||||
|
public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem;
|
||||||
@@ -33,6 +33,7 @@ public sealed class VendingMachineSystem : SharedVendingMachineSystem
|
|||||||
component.EjectEnd = state.EjectEnd;
|
component.EjectEnd = state.EjectEnd;
|
||||||
component.DenyEnd = state.DenyEnd;
|
component.DenyEnd = state.DenyEnd;
|
||||||
component.DispenseOnHitEnd = state.DispenseOnHitEnd;
|
component.DispenseOnHitEnd = state.DispenseOnHitEnd;
|
||||||
|
component.Broken = state.Broken;
|
||||||
|
|
||||||
// If all we did was update amounts then we can leave BUI buttons in place.
|
// If all we did was update amounts then we can leave BUI buttons in place.
|
||||||
var fullUiUpdate = !component.Inventory.Keys.SequenceEqual(state.Inventory.Keys) ||
|
var fullUiUpdate = !component.Inventory.Keys.SequenceEqual(state.Inventory.Keys) ||
|
||||||
|
|||||||
243
Content.IntegrationTests/Tests/Construction/RCDTest.cs
Normal file
243
Content.IntegrationTests/Tests/Construction/RCDTest.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.IntegrationTests.Tests.Interaction;
|
||||||
|
using Content.Shared.Charges.Systems;
|
||||||
|
using Content.Shared.RCD;
|
||||||
|
using Content.Shared.RCD.Components;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.Construction;
|
||||||
|
|
||||||
|
public sealed class RCDTest : InteractionTest
|
||||||
|
{
|
||||||
|
private static readonly EntProtoId RCDProtoId = "RCD";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingWall = "WallSolid";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingAirlock = "Airlock";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingPlating = "Plating";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingFloorSteel = "FloorSteel";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingDeconstruct = "Deconstruct";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingDeconstructTile = "DeconstructTile";
|
||||||
|
private static readonly ProtoId<RCDPrototype> RCDSettingDeconstructLattice = "DeconstructLattice";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests RCD construction and deconstruction, as well as selecting options from the radial menu.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task RCDConstructionDeconstructionTest()
|
||||||
|
{
|
||||||
|
// Place some tiles around the player so that we have space to build.
|
||||||
|
var pNorth = new EntityCoordinates(SPlayer, new Vector2(0, 1));
|
||||||
|
var pSouth = new EntityCoordinates(SPlayer, new Vector2(0, -1));
|
||||||
|
var pEast = new EntityCoordinates(SPlayer, new Vector2(1, 0));
|
||||||
|
var pWest = new EntityCoordinates(SPlayer, new Vector2(-1, 0));
|
||||||
|
|
||||||
|
// Use EntityCoordinates relative to the grid because the player turns around when interacting.
|
||||||
|
pNorth = Transform.WithEntityId(pNorth, MapData.Grid);
|
||||||
|
pSouth = Transform.WithEntityId(pSouth, MapData.Grid);
|
||||||
|
pEast = Transform.WithEntityId(pEast, MapData.Grid);
|
||||||
|
pWest = Transform.WithEntityId(pWest, MapData.Grid);
|
||||||
|
|
||||||
|
await SetTile(Plating, SEntMan.GetNetCoordinates(pNorth), MapData.Grid);
|
||||||
|
await SetTile(Plating, SEntMan.GetNetCoordinates(pSouth), MapData.Grid);
|
||||||
|
await SetTile(Plating, SEntMan.GetNetCoordinates(pEast), MapData.Grid);
|
||||||
|
await SetTile(Lattice, SEntMan.GetNetCoordinates(pWest), MapData.Grid);
|
||||||
|
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingWall, out var settingWall), $"RCDPrototype not found: {RCDSettingWall}.");
|
||||||
|
Assert.That(settingWall.Prototype, Is.Not.Null, "RCDPrototype has a null spawning prototype.");
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingAirlock, out var settingAirlock), $"RCDPrototype not found: {RCDSettingAirlock}.");
|
||||||
|
Assert.That(settingAirlock.Prototype, Is.Not.Null, $"RCDPrototype has a null spawning prototype.");
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingPlating, out var settingPlating), $"RCDPrototype not found: {RCDSettingPlating}.");
|
||||||
|
Assert.That(settingPlating.Prototype, Is.Not.Null, "RCDPrototype has a null spawning prototype.");
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingFloorSteel, out var settingFloorSteel), $"RCDPrototype not found: {RCDSettingFloorSteel}.");
|
||||||
|
Assert.That(settingFloorSteel.Prototype, Is.Not.Null, "RCDPrototype has a null spawning prototype.");
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingDeconstructTile, out var settingDeconstructTile), $"RCDPrototype not found: {RCDSettingDeconstructTile}.");
|
||||||
|
Assert.That(ProtoMan.TryIndex(RCDSettingDeconstructLattice, out var settingDeconstructLattice), $"RCDPrototype not found: {RCDSettingDeconstructLattice}.");
|
||||||
|
|
||||||
|
var rcd = await PlaceInHands(RCDProtoId);
|
||||||
|
|
||||||
|
// Give the RCD enough charges to do everything.
|
||||||
|
var sCharges = SEntMan.System<SharedChargesSystem>();
|
||||||
|
await Server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
sCharges.SetMaxCharges(ToServer(rcd), 10000);
|
||||||
|
sCharges.SetCharges(ToServer(rcd), 10000);
|
||||||
|
});
|
||||||
|
var initialCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges, Is.EqualTo(10000), "RCD did not have the correct amount of charges.");
|
||||||
|
|
||||||
|
// Make sure that picking it up did not open the UI.
|
||||||
|
Assert.That(IsUiOpen(RcdUiKey.Key), Is.False, "RCD UI was opened when picking it up.");
|
||||||
|
|
||||||
|
// Switch to building walls.
|
||||||
|
await SetRcdProto(rcd, RCDSettingWall);
|
||||||
|
|
||||||
|
// Build a wall next to the player.
|
||||||
|
await Interact(null, pNorth);
|
||||||
|
|
||||||
|
// Check that there is exactly one wall.
|
||||||
|
await RunSeconds(settingWall.Delay + 1); // wait for the construction to finish
|
||||||
|
await AssertEntityLookup((settingWall.Prototype, 1));
|
||||||
|
|
||||||
|
// Check that the wall is in the correct tile.
|
||||||
|
var wallUid = await FindEntity(settingWall.Prototype);
|
||||||
|
var wallNetUid = FromServer(wallUid);
|
||||||
|
AssertLocation(wallNetUid, FromServer(pNorth));
|
||||||
|
|
||||||
|
// Check that the cost of the wall was subtracted from the current charges.
|
||||||
|
var newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingWall.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after building something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Try building another wall in the same spot.
|
||||||
|
await Interact(null, pNorth);
|
||||||
|
await RunSeconds(settingWall.Delay + 1); // wait for the construction to finish
|
||||||
|
|
||||||
|
// Check that there is still exactly one wall.
|
||||||
|
await AssertEntityLookup((settingWall.Prototype, 1));
|
||||||
|
|
||||||
|
// Check that the failed construction did not cost us any charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges, Is.EqualTo(newCharges), "RCD has wrong amount of charges after failing to build something.");
|
||||||
|
|
||||||
|
// Switch to building airlocks.
|
||||||
|
await SetRcdProto(rcd, RCDSettingAirlock);
|
||||||
|
|
||||||
|
// Build an airlock next to the player.
|
||||||
|
await Interact(null, pSouth);
|
||||||
|
|
||||||
|
// Check that there is exactly one airlock.
|
||||||
|
await RunSeconds(settingAirlock.Delay + 1); // wait for the construction to finish
|
||||||
|
await AssertEntityLookup(
|
||||||
|
(settingWall.Prototype, 1),
|
||||||
|
(settingAirlock.Prototype, 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check that the airlock is in the correct tile.
|
||||||
|
var airlockUid = await FindEntity(settingAirlock.Prototype);
|
||||||
|
var airlockNetUid = FromServer(airlockUid);
|
||||||
|
AssertLocation(airlockNetUid, FromServer(pSouth));
|
||||||
|
|
||||||
|
// Check that the cost of the airlock was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingAirlock.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after building something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Switch to building plating.
|
||||||
|
await SetRcdProto(rcd, RCDSettingPlating);
|
||||||
|
|
||||||
|
// Try building plating on existing plating.
|
||||||
|
await AssertTile(settingPlating.Prototype, FromServer(pEast));
|
||||||
|
await Interact(null, pEast);
|
||||||
|
|
||||||
|
// Check that the tile did not change.
|
||||||
|
await AssertTile(settingPlating.Prototype, FromServer(pEast));
|
||||||
|
|
||||||
|
// Check that the failed construction did not cost us any charges.
|
||||||
|
await RunSeconds(settingPlating.Delay + 1); // wait for the construction to finish
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges, Is.EqualTo(newCharges), "RCD has wrong amount of charges after failing to build something.");
|
||||||
|
|
||||||
|
// Try building plating on top of lattice.
|
||||||
|
await AssertTile(Lattice, FromServer(pWest));
|
||||||
|
await Interact(null, pWest);
|
||||||
|
await RunSeconds(settingPlating.Delay + 1); // wait for the construction to finish
|
||||||
|
|
||||||
|
// Check that the tile is now plating.
|
||||||
|
await AssertTile(settingPlating.Prototype, FromServer(pWest));
|
||||||
|
|
||||||
|
// Check that the cost of the plating was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingPlating.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after building something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Switch to building steel tiles.
|
||||||
|
await SetRcdProto(rcd, RCDSettingFloorSteel);
|
||||||
|
|
||||||
|
// Try building a steel tile on top of plating.
|
||||||
|
await Interact(null, pEast);
|
||||||
|
|
||||||
|
// Check that the tile is now a steel tile.
|
||||||
|
await RunSeconds(settingFloorSteel.Delay + 1); // wait for the construction to finish
|
||||||
|
await AssertTile(settingFloorSteel.Prototype, FromServer(pEast));
|
||||||
|
|
||||||
|
// Check that the cost of the plating was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingFloorSteel.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after building something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Switch to deconstruction mode.
|
||||||
|
await SetRcdProto(rcd, RCDSettingDeconstruct);
|
||||||
|
|
||||||
|
// Deconstruct the wall.
|
||||||
|
Assert.That(SEntMan.TryGetComponent<RCDDeconstructableComponent>(wallUid, out var wallComp), "Wall entity did not have the RCDDeconstructableComponent.");
|
||||||
|
await Interact(wallUid, pNorth);
|
||||||
|
await RunSeconds(wallComp.Delay + 1); // wait for the deconstruction to finish
|
||||||
|
AssertDeleted(wallNetUid);
|
||||||
|
|
||||||
|
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - wallComp.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after deconstructing something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Deconstruct the airlock.
|
||||||
|
Assert.That(SEntMan.TryGetComponent<RCDDeconstructableComponent>(airlockUid, out var airlockComp), "Wall entity did not have the RCDDeconstructableComponent.");
|
||||||
|
await Interact(airlockUid, pSouth);
|
||||||
|
await RunSeconds(airlockComp.Delay + 1); // wait for the deconstruction to finish
|
||||||
|
AssertDeleted(airlockNetUid);
|
||||||
|
|
||||||
|
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - airlockComp.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after deconstructing something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Deconstruct the steel tile.
|
||||||
|
await Interact(null, pEast);
|
||||||
|
await RunSeconds(settingDeconstructTile.Delay + 1); // wait for the deconstruction to finish
|
||||||
|
await AssertTile(Lattice, FromServer(pEast));
|
||||||
|
|
||||||
|
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingDeconstructTile.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after deconstructing something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Deconstruct the plating.
|
||||||
|
await Interact(null, pWest);
|
||||||
|
await RunSeconds(settingDeconstructTile.Delay + 1); // wait for the deconstruction to finish
|
||||||
|
await AssertTile(Lattice, FromServer(pWest));
|
||||||
|
|
||||||
|
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingDeconstructTile.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after deconstructing something.");
|
||||||
|
initialCharges = newCharges;
|
||||||
|
|
||||||
|
// Deconstruct the lattice.
|
||||||
|
await Interact(null, pWest);
|
||||||
|
await RunSeconds(settingDeconstructLattice.Delay + 1); // wait for the deconstruction to finish
|
||||||
|
await AssertTile(null, FromServer(pWest));
|
||||||
|
|
||||||
|
// Check that the cost of the deconstruction was subtracted from the current charges.
|
||||||
|
newCharges = sCharges.GetCurrentCharges(ToServer(rcd));
|
||||||
|
Assert.That(initialCharges - settingDeconstructLattice.Cost, Is.EqualTo(newCharges), "RCD has wrong amount of charges after deconstructing something.");
|
||||||
|
|
||||||
|
// Wait for the visual effect to disappear.
|
||||||
|
await RunSeconds(3);
|
||||||
|
|
||||||
|
// Check that there are no entities left.
|
||||||
|
await AssertEntityLookup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetRcdProto(NetEntity rcd, ProtoId<RCDPrototype> protoId)
|
||||||
|
{
|
||||||
|
await UseInHand();
|
||||||
|
await RunTicks(3);
|
||||||
|
Assert.That(IsUiOpen(RcdUiKey.Key), Is.True, "RCD UI was not opened when using the RCD while holding it.");
|
||||||
|
|
||||||
|
// Simulating a click on the right control for nested radial menus is very complicated.
|
||||||
|
// So we just manually send a networking message from the client to tell the server we selected an option.
|
||||||
|
// TODO: Write a separate test for clicking through a SimpleRadialMenu.
|
||||||
|
await SendBui(RcdUiKey.Key, new RCDSystemMessage(protoId), rcd);
|
||||||
|
await CloseBui(RcdUiKey.Key, rcd);
|
||||||
|
Assert.That(IsUiOpen(RcdUiKey.Key), Is.False, "RCD UI is still open.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -726,7 +726,7 @@ public abstract partial class InteractionTest
|
|||||||
tile = MapSystem.GetTileRef(gridUid, grid, serverCoords).Tile;
|
tile = MapSystem.GetTileRef(gridUid, grid, serverCoords).Tile;
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(tile.TypeId, Is.EqualTo(targetTile.TypeId));
|
Assert.That(tile.TypeId, Is.EqualTo(targetTile.TypeId), $"Expected tile at NetCoordinates {coords}: {TileMan[targetTile.TypeId].Name}. But was: {TileMan[tile.TypeId].Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AssertGridCount(int value)
|
protected void AssertGridCount(int value)
|
||||||
@@ -742,6 +742,20 @@ public abstract partial class InteractionTest
|
|||||||
Assert.That(count, Is.EqualTo(value));
|
Assert.That(count, Is.EqualTo(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check that some entity is close to a certain coordinate location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The entity to check the location for. Defaults to <see cref="target"/></param>
|
||||||
|
/// <param name="coordinates">The coordinates the entity should be at.</param>
|
||||||
|
/// <param name="radius">The maximum allowed distance from the target coords</param>
|
||||||
|
protected void AssertLocation(NetEntity? target, NetCoordinates coords, float radius = 0.01f)
|
||||||
|
{
|
||||||
|
target ??= Target;
|
||||||
|
Assert.That(target, Is.Not.Null, "No target specified");
|
||||||
|
Assert.That(Position(target!.Value).TryDelta(SEntMan, Transform, ToServer(coords), out var delta), "Could not calculate distance between coordinates.");
|
||||||
|
Assert.That(delta.Length(), Is.LessThanOrEqualTo(radius), $"{SEntMan.ToPrettyString(SEntMan.GetEntity(target.Value))} was not at the intended location. Distance: {delta}, allowed distance: {radius}");
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Entity lookups
|
#region Entity lookups
|
||||||
@@ -986,9 +1000,9 @@ public abstract partial class InteractionTest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a bui message using the given bui key.
|
/// Sends a bui message using the given bui key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task SendBui(Enum key, BoundUserInterfaceMessage msg, EntityUid? _ = null)
|
protected async Task SendBui(Enum key, BoundUserInterfaceMessage msg, NetEntity? target = null)
|
||||||
{
|
{
|
||||||
if (!TryGetBui(key, out var bui))
|
if (!TryGetBui(key, out var bui, target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Client.WaitPost(() => bui.SendMessage(msg));
|
await Client.WaitPost(() => bui.SendMessage(msg));
|
||||||
@@ -1000,9 +1014,9 @@ public abstract partial class InteractionTest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a bui message using the given bui key.
|
/// Sends a bui message using the given bui key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task CloseBui(Enum key, EntityUid? _ = null)
|
protected async Task CloseBui(Enum key, NetEntity? target = null)
|
||||||
{
|
{
|
||||||
if (!TryGetBui(key, out var bui))
|
if (!TryGetBui(key, out var bui, target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Client.WaitPost(() => bui.Close());
|
await Client.WaitPost(() => bui.Close());
|
||||||
@@ -1424,15 +1438,25 @@ public abstract partial class InteractionTest
|
|||||||
protected EntityUid? ToServer(NetEntity? nent) => SEntMan.GetEntity(nent);
|
protected EntityUid? ToServer(NetEntity? nent) => SEntMan.GetEntity(nent);
|
||||||
protected EntityUid? ToClient(NetEntity? nent) => CEntMan.GetEntity(nent);
|
protected EntityUid? ToClient(NetEntity? nent) => CEntMan.GetEntity(nent);
|
||||||
protected EntityUid ToServer(EntityUid cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
protected EntityUid ToServer(EntityUid cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
||||||
protected EntityUid ToClient(EntityUid cuid) => CEntMan.GetEntity(SEntMan.GetNetEntity(cuid));
|
protected EntityUid ToClient(EntityUid suid) => CEntMan.GetEntity(SEntMan.GetNetEntity(suid));
|
||||||
protected EntityUid? ToServer(EntityUid? cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
protected EntityUid? ToServer(EntityUid? cuid) => SEntMan.GetEntity(CEntMan.GetNetEntity(cuid));
|
||||||
protected EntityUid? ToClient(EntityUid? cuid) => CEntMan.GetEntity(SEntMan.GetNetEntity(cuid));
|
protected EntityUid? ToClient(EntityUid? suid) => CEntMan.GetEntity(SEntMan.GetNetEntity(suid));
|
||||||
|
|
||||||
protected EntityCoordinates ToServer(NetCoordinates coords) => SEntMan.GetCoordinates(coords);
|
protected EntityCoordinates ToServer(NetCoordinates coords) => SEntMan.GetCoordinates(coords);
|
||||||
protected EntityCoordinates ToClient(NetCoordinates coords) => CEntMan.GetCoordinates(coords);
|
protected EntityCoordinates ToClient(NetCoordinates coords) => CEntMan.GetCoordinates(coords);
|
||||||
protected EntityCoordinates? ToServer(NetCoordinates? coords) => SEntMan.GetCoordinates(coords);
|
protected EntityCoordinates? ToServer(NetCoordinates? coords) => SEntMan.GetCoordinates(coords);
|
||||||
protected EntityCoordinates? ToClient(NetCoordinates? coords) => CEntMan.GetCoordinates(coords);
|
protected EntityCoordinates? ToClient(NetCoordinates? coords) => CEntMan.GetCoordinates(coords);
|
||||||
|
|
||||||
|
protected NetEntity FromServer(EntityUid suid) => SEntMan.GetNetEntity(suid);
|
||||||
|
protected NetEntity FromClient(EntityUid cuid) => CEntMan.GetNetEntity(cuid);
|
||||||
|
protected NetEntity? FromServer(EntityUid? suid) => SEntMan.GetNetEntity(suid);
|
||||||
|
protected NetEntity? FromClient(EntityUid? cuid) => CEntMan.GetNetEntity(cuid);
|
||||||
|
|
||||||
|
protected NetCoordinates FromServer(EntityCoordinates scoords) => SEntMan.GetNetCoordinates(scoords);
|
||||||
|
protected NetCoordinates FromClient(EntityCoordinates ccoords) => CEntMan.GetNetCoordinates(ccoords);
|
||||||
|
protected NetCoordinates? FromServer(EntityCoordinates? scoords) => SEntMan.GetNetCoordinates(scoords);
|
||||||
|
protected NetCoordinates? FromClient(EntityCoordinates? ccoords) => CEntMan.GetNetCoordinates(ccoords);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Metadata & Transforms
|
#region Metadata & Transforms
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using Content.Server.NodeContainer;
|
|
||||||
using Content.Server.NodeContainer.EntitySystems;
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
using Content.Server.NodeContainer.Nodes;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Power.Nodes;
|
using Content.Server.Power.Nodes;
|
||||||
using Content.Shared.Coordinates;
|
using Content.Shared.Coordinates;
|
||||||
using Content.Shared.NodeContainer;
|
using Content.Shared.NodeContainer;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Content.Server.Maps;
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.NodeGroups;
|
using Content.Server.Power.NodeGroups;
|
||||||
using Content.Server.Power.Pow3r;
|
using Content.Server.Power.Pow3r;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.NodeContainer;
|
using Content.Shared.NodeContainer;
|
||||||
using Robust.Shared.EntitySerialization;
|
using Robust.Shared.EntitySerialization;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Server.Body.Systems;
|
|||||||
using Content.Server.Electrocution;
|
using Content.Server.Electrocution;
|
||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.GhostKick;
|
using Content.Server.GhostKick;
|
||||||
using Content.Server.Medical;
|
|
||||||
using Content.Server.Nutrition.EntitySystems;
|
using Content.Server.Nutrition.EntitySystems;
|
||||||
using Content.Server.Physics.Components;
|
using Content.Server.Physics.Components;
|
||||||
using Content.Server.Pointing.Components;
|
using Content.Server.Pointing.Components;
|
||||||
@@ -32,6 +31,7 @@ using Content.Shared.Electrocution;
|
|||||||
using Content.Shared.Gravity;
|
using Content.Shared.Gravity;
|
||||||
using Content.Shared.Interaction.Components;
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Medical;
|
||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ using Content.Shared.Doors.Components;
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Station.Components;
|
using Content.Shared.Station.Components;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Clothing.EntitySystems;
|
||||||
using Content.Shared.Emp;
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.IdentityManagement.Components;
|
using Content.Shared.IdentityManagement.Components;
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Prototypes;
|
using Content.Shared.Prototypes;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.Clothing.Systems;
|
namespace Content.Server.Clothing.Systems;
|
||||||
|
|
||||||
@@ -16,15 +12,12 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<ChameleonClothingComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, ChameleonPrototypeSelectedMessage>(OnSelected);
|
SubscribeLocalEvent<ChameleonClothingComponent, ChameleonPrototypeSelectedMessage>(OnSelected);
|
||||||
|
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, ChameleonClothingComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, ChameleonClothingComponent component, MapInitEvent args)
|
||||||
@@ -37,21 +30,6 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||||||
SetSelectedPrototype(uid, args.SelectedId, component: component);
|
SetSelectedPrototype(uid, args.SelectedId, component: component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, ChameleonClothingComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
if (!component.AffectedByEmp)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (component.EmpContinuous)
|
|
||||||
component.NextEmpChange = _timing.CurTime + TimeSpan.FromSeconds(1f / component.EmpChangeIntensity);
|
|
||||||
|
|
||||||
var pick = GetRandomValidPrototype(component.Slot, component.RequireTag);
|
|
||||||
SetSelectedPrototype(uid, pick, component: component);
|
|
||||||
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUi(EntityUid uid, ChameleonClothingComponent? component = null)
|
private void UpdateUi(EntityUid uid, ChameleonClothingComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component))
|
||||||
@@ -64,7 +42,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change chameleon items name, description and sprite to mimic other entity prototype.
|
/// Change chameleon items name, description and sprite to mimic other entity prototype.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false,
|
public override void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false,
|
||||||
ChameleonClothingComponent? component = null)
|
ChameleonClothingComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component, false))
|
if (!Resolve(uid, ref component, false))
|
||||||
@@ -88,14 +66,6 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a random prototype for a given slot.
|
|
||||||
/// </summary>
|
|
||||||
public string GetRandomValidPrototype(SlotFlags slot, string? tag = null)
|
|
||||||
{
|
|
||||||
return _random.Pick(GetValidTargets(slot, tag).ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -106,7 +76,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
|||||||
if (!chameleon.EmpContinuous)
|
if (!chameleon.EmpContinuous)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (_timing.CurTime < chameleon.NextEmpChange)
|
if (Timing.CurTime < chameleon.NextEmpChange)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// randomly pick cloth element from available and apply it
|
// randomly pick cloth element from available and apply it
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Mech.Systems;
|
using Content.Server.Mech.Systems;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.Mech.Components;
|
using Content.Shared.Mech.Components;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.Containers;
|
using Robust.Server.Containers;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Medical;
|
using Content.Shared.Medical;
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,11 @@ using Content.Server.Power.EntitySystems;
|
|||||||
using Content.Server.Radio;
|
using Content.Server.Radio;
|
||||||
using Content.Server.SurveillanceCamera;
|
using Content.Server.SurveillanceCamera;
|
||||||
using Content.Shared.Emp;
|
using Content.Shared.Emp;
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Server.Emp;
|
namespace Content.Server.Emp;
|
||||||
|
|
||||||
public sealed class EmpSystem : SharedEmpSystem
|
public sealed class EmpSystem : SharedEmpSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
|
||||||
|
|
||||||
public const string EmpPulseEffectPrototype = "EffectEmpPulse";
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -22,84 +17,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
|||||||
SubscribeLocalEvent<EmpDisabledComponent, SurveillanceCameraSetActiveAttemptEvent>(OnCameraSetActive);
|
SubscribeLocalEvent<EmpDisabledComponent, SurveillanceCameraSetActiveAttemptEvent>(OnCameraSetActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
|
|
||||||
{
|
|
||||||
foreach (var uid in _lookup.GetEntitiesInRange(coordinates, range))
|
|
||||||
{
|
|
||||||
TryEmpEffects(uid, energyConsumption, duration);
|
|
||||||
}
|
|
||||||
Spawn(EmpPulseEffectPrototype, coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="coordinates">The location to trigger the EMP pulse at.</param>
|
|
||||||
/// <param name="range">The range of the EMP pulse.</param>
|
|
||||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
|
|
||||||
/// <param name="duration">The duration of the EMP effects.</param>
|
|
||||||
public void EmpPulse(EntityCoordinates coordinates, float range, float energyConsumption, float duration)
|
|
||||||
{
|
|
||||||
foreach (var uid in _lookup.GetEntitiesInRange(coordinates, range))
|
|
||||||
{
|
|
||||||
TryEmpEffects(uid, energyConsumption, duration);
|
|
||||||
}
|
|
||||||
Spawn(EmpPulseEffectPrototype, coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to apply the effects of an EMP pulse onto an entity by first raising an <see cref="EmpAttemptEvent"/>, followed by raising a <see cref="EmpPulseEvent"/> on it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The entity to apply the EMP effects on.</param>
|
|
||||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
|
|
||||||
/// <param name="duration">The duration of the EMP effects.</param>
|
|
||||||
public void TryEmpEffects(EntityUid uid, float energyConsumption, float duration)
|
|
||||||
{
|
|
||||||
var attemptEv = new EmpAttemptEvent();
|
|
||||||
RaiseLocalEvent(uid, attemptEv);
|
|
||||||
if (attemptEv.Cancelled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DoEmpEffects(uid, energyConsumption, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the effects of an EMP pulse onto an entity by raising a <see cref="EmpPulseEvent"/> on it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uid">The entity to apply the EMP effects on.</param>
|
|
||||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
|
|
||||||
/// <param name="duration">The duration of the EMP effects.</param>
|
|
||||||
public void DoEmpEffects(EntityUid uid, float energyConsumption, float duration)
|
|
||||||
{
|
|
||||||
var ev = new EmpPulseEvent(energyConsumption, false, false, TimeSpan.FromSeconds(duration));
|
|
||||||
RaiseLocalEvent(uid, ref ev);
|
|
||||||
|
|
||||||
if (ev.Affected)
|
|
||||||
Spawn(EmpDisabledEffectPrototype, Transform(uid).Coordinates);
|
|
||||||
|
|
||||||
if (!ev.Disabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var disabled = EnsureComp<EmpDisabledComponent>(uid);
|
|
||||||
disabled.DisabledUntil = Timing.CurTime + TimeSpan.FromSeconds(duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<EmpDisabledComponent>();
|
|
||||||
while (query.MoveNext(out var uid, out var comp))
|
|
||||||
{
|
|
||||||
if (comp.DisabledUntil < Timing.CurTime)
|
|
||||||
{
|
|
||||||
RemComp<EmpDisabledComponent>(uid);
|
|
||||||
var ev = new EmpDisabledRemoved();
|
|
||||||
RaiseLocalEvent(uid, ref ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRadioSendAttempt(EntityUid uid, EmpDisabledComponent component, ref RadioSendAttemptEvent args)
|
private void OnRadioSendAttempt(EntityUid uid, EmpDisabledComponent component, ref RadioSendAttemptEvent args)
|
||||||
{
|
{
|
||||||
args.Cancelled = true;
|
args.Cancelled = true;
|
||||||
@@ -120,14 +37,3 @@ public sealed class EmpSystem : SharedEmpSystem
|
|||||||
args.Cancelled = true;
|
args.Cancelled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised on an entity before <see cref="EmpPulseEvent"/>. Cancel this to prevent the emp event being raised.
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class EmpAttemptEvent : CancellableEntityEventArgs;
|
|
||||||
|
|
||||||
[ByRefEvent]
|
|
||||||
public record struct EmpPulseEvent(float EnergyConsumption, bool Affected, bool Disabled, TimeSpan Duration);
|
|
||||||
|
|
||||||
[ByRefEvent]
|
|
||||||
public record struct EmpDisabledRemoved();
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using Content.Server.Emp;
|
|||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.Fluids.EntitySystems;
|
using Content.Server.Fluids.EntitySystems;
|
||||||
using Content.Server.Ghost.Roles.Components;
|
using Content.Server.Ghost.Roles.Components;
|
||||||
using Content.Server.Medical;
|
|
||||||
using Content.Server.Polymorph.Components;
|
using Content.Server.Polymorph.Components;
|
||||||
using Content.Server.Polymorph.Systems;
|
using Content.Server.Polymorph.Systems;
|
||||||
using Content.Server.Speech.Components;
|
using Content.Server.Speech.Components;
|
||||||
@@ -29,6 +28,7 @@ using Content.Shared.EntityEffects.Effects;
|
|||||||
using Content.Shared.EntityEffects;
|
using Content.Shared.EntityEffects;
|
||||||
using Content.Shared.Flash;
|
using Content.Shared.Flash;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
|
using Content.Shared.Medical;
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Random;
|
using Content.Shared.Random;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Coordinates.Helpers;
|
using Content.Shared.Coordinates.Helpers;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
|
|
||||||
namespace Content.Server.Holosign;
|
namespace Content.Server.Holosign;
|
||||||
@@ -12,7 +12,6 @@ public sealed class HolosignSystem : EntitySystem
|
|||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Content.Shared.Examine;
|
|||||||
using Content.Shared.Light;
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Light.Components;
|
using Content.Shared.Light.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Station.Components;
|
using Content.Shared.Station.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Color = Robust.Shared.Maths.Color;
|
using Color = Robust.Shared.Maths.Color;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Ghost;
|
using Content.Server.Ghost;
|
||||||
using Content.Shared.Light.Components;
|
using Content.Shared.Light.Components;
|
||||||
using Content.Shared.Light.EntitySystems;
|
using Content.Shared.Light.EntitySystems;
|
||||||
@@ -16,8 +15,6 @@ public sealed class PoweredLightSystem : SharedPoweredLightSystem
|
|||||||
SubscribeLocalEvent<PoweredLightComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<PoweredLightComponent, MapInitEvent>(OnMapInit);
|
||||||
|
|
||||||
SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
|
SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
|
||||||
|
|
||||||
SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)
|
private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)
|
||||||
@@ -55,10 +52,4 @@ public sealed class PoweredLightSystem : SharedPoweredLightSystem
|
|||||||
// need this to update visualizers
|
// need this to update visualizers
|
||||||
UpdateLight(uid, light);
|
UpdateLight(uid, light);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
if (TryDestroyBulb(uid, component))
|
|
||||||
args.Affected = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System.Linq;
|
|||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Mech.Components;
|
using Content.Server.Mech.Components;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -14,6 +13,7 @@ using Content.Shared.Mech.Components;
|
|||||||
using Content.Shared.Mech.EntitySystems;
|
using Content.Shared.Mech.EntitySystems;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Tools;
|
using Content.Shared.Tools;
|
||||||
using Content.Shared.Tools.Components;
|
using Content.Shared.Tools.Components;
|
||||||
using Content.Shared.Tools.Systems;
|
using Content.Shared.Tools.Systems;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using Content.Server.DeviceNetwork.Systems;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Medical.CrewMonitoring;
|
using Content.Server.Medical.CrewMonitoring;
|
||||||
using Content.Shared.DeviceNetwork.Components;
|
using Content.Shared.DeviceNetwork.Components;
|
||||||
using Content.Shared.Medical.SuitSensor;
|
|
||||||
using Content.Shared.Medical.SuitSensors;
|
using Content.Shared.Medical.SuitSensors;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -14,14 +12,6 @@ public sealed class SuitSensorSystem : SharedSuitSensorSystem
|
|||||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||||
[Dependency] private readonly SingletonDeviceNetServerSystem _singletonServerSystem = default!;
|
[Dependency] private readonly SingletonDeviceNetServerSystem _singletonServerSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SuitSensorComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
SubscribeLocalEvent<SuitSensorComponent, EmpDisabledRemoved>(OnEmpFinished);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -70,22 +60,4 @@ public sealed class SuitSensorSystem : SharedSuitSensorSystem
|
|||||||
_deviceNetworkSystem.QueuePacket(uid, sensor.ConnectedServer, payload, device: device);
|
_deviceNetworkSystem.QueuePacket(uid, sensor.ConnectedServer, payload, device: device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(Entity<SuitSensorComponent> ent, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
|
|
||||||
ent.Comp.PreviousMode = ent.Comp.Mode;
|
|
||||||
SetSensor(ent.AsNullable(), SuitSensorMode.SensorOff, null);
|
|
||||||
|
|
||||||
ent.Comp.PreviousControlsLocked = ent.Comp.ControlsLocked;
|
|
||||||
ent.Comp.ControlsLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEmpFinished(Entity<SuitSensorComponent> ent, ref EmpDisabledRemoved args)
|
|
||||||
{
|
|
||||||
SetSensor(ent.AsNullable(), ent.Comp.PreviousMode, null);
|
|
||||||
ent.Comp.ControlsLocked = ent.Comp.PreviousControlsLocked;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
using Content.Server.Body.Systems;
|
|
||||||
using Content.Server.Fluids.EntitySystems;
|
|
||||||
using Content.Server.Forensics;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Body.Systems;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
|
||||||
using Content.Shared.Chemistry.Reagent;
|
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
using Content.Shared.Mobs.Systems;
|
|
||||||
using Content.Shared.Movement.Systems;
|
|
||||||
using Content.Shared.Nutrition.Components;
|
|
||||||
using Content.Shared.Nutrition.EntitySystems;
|
|
||||||
using Robust.Server.Audio;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Medical
|
|
||||||
{
|
|
||||||
public sealed class VomitSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
|
||||||
[Dependency] private readonly BodySystem _body = default!;
|
|
||||||
[Dependency] private readonly ForensicsSystem _forensics = default!;
|
|
||||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
|
||||||
[Dependency] private readonly MobStateSystem _mobstate = default!;
|
|
||||||
[Dependency] private readonly MovementModStatusSystem _movementMod = default!;
|
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
|
||||||
[Dependency] private readonly PuddleSystem _puddle = default!;
|
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
|
||||||
[Dependency] private readonly ThirstSystem _thirst = default!;
|
|
||||||
|
|
||||||
private static readonly ProtoId<SoundCollectionPrototype> VomitCollection = "Vomit";
|
|
||||||
|
|
||||||
private readonly SoundSpecifier _vomitSound = new SoundCollectionSpecifier(VomitCollection,
|
|
||||||
AudioParams.Default.WithVariation(0.2f).WithVolume(-4f));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Make an entity vomit, if they have a stomach.
|
|
||||||
/// </summary>
|
|
||||||
public void Vomit(EntityUid uid, float thirstAdded = -40f, float hungerAdded = -40f, bool force = false)
|
|
||||||
{
|
|
||||||
// Main requirement: You have a stomach
|
|
||||||
var stomachList = _body.GetBodyOrganEntityComps<StomachComponent>(uid);
|
|
||||||
if (stomachList.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Vomit only if entity is alive
|
|
||||||
// Ignore condition if force was set to true
|
|
||||||
if (!force && _mobstate.IsDead(uid))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Vomiting makes you hungrier and thirstier
|
|
||||||
if (TryComp<HungerComponent>(uid, out var hunger))
|
|
||||||
_hunger.ModifyHunger(uid, hungerAdded, hunger);
|
|
||||||
|
|
||||||
if (TryComp<ThirstComponent>(uid, out var thirst))
|
|
||||||
_thirst.ModifyThirst(uid, thirst, thirstAdded);
|
|
||||||
|
|
||||||
// It fully empties the stomach, this amount from the chem stream is relatively small
|
|
||||||
var solutionSize = (MathF.Abs(thirstAdded) + MathF.Abs(hungerAdded)) / 6;
|
|
||||||
// Apply a bit of slowdown
|
|
||||||
_movementMod.TryUpdateMovementSpeedModDuration(uid, MovementModStatusSystem.VomitingSlowdown, TimeSpan.FromSeconds(solutionSize), 0.5f);
|
|
||||||
|
|
||||||
// TODO: Need decals
|
|
||||||
var solution = new Solution();
|
|
||||||
|
|
||||||
// Empty the stomach out into it
|
|
||||||
foreach (var stomach in stomachList)
|
|
||||||
{
|
|
||||||
if (_solutionContainer.ResolveSolution(stomach.Owner, StomachSystem.DefaultSolutionName, ref stomach.Comp1.Solution, out var sol))
|
|
||||||
{
|
|
||||||
solution.AddSolution(sol, _proto);
|
|
||||||
sol.RemoveAllSolution();
|
|
||||||
_solutionContainer.UpdateChemicals(stomach.Comp1.Solution.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Adds a tiny amount of the chem stream from earlier along with vomit
|
|
||||||
if (TryComp<BloodstreamComponent>(uid, out var bloodStream))
|
|
||||||
{
|
|
||||||
const float chemMultiplier = 0.1f;
|
|
||||||
|
|
||||||
var vomitAmount = solutionSize;
|
|
||||||
|
|
||||||
// Takes 10% of the chemicals removed from the chem stream
|
|
||||||
if (_solutionContainer.ResolveSolution(uid, bloodStream.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
|
|
||||||
{
|
|
||||||
var vomitChemstreamAmount = _solutionContainer.SplitSolution(bloodStream.ChemicalSolution.Value, vomitAmount);
|
|
||||||
vomitChemstreamAmount.ScaleSolution(chemMultiplier);
|
|
||||||
solution.AddSolution(vomitChemstreamAmount, _proto);
|
|
||||||
|
|
||||||
vomitAmount -= (float)vomitChemstreamAmount.Volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
|
|
||||||
solution.AddReagent(new ReagentId("Vomit", _bloodstream.GetEntityBloodData(uid)), vomitAmount); // TODO: Dehardcode vomit prototype
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_puddle.TrySpillAt(uid, solution, out var puddle, false))
|
|
||||||
{
|
|
||||||
_forensics.TransferDna(puddle, uid, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force sound to play as spill doesn't work if solution is empty.
|
|
||||||
_audio.PlayPvs(_vomitSound, uid);
|
|
||||||
_popup.PopupEntity(Loc.GetString("disease-vomit", ("person", Identity.Entity(uid, EntityManager))), uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ using Content.Shared.Interaction;
|
|||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Shared.Audio;
|
using Content.Shared.Power.Components;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Ninja.Events;
|
using Content.Server.Ninja.Events;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ namespace Content.Server.Ninja.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly EmpSystem _emp = default!;
|
[Dependency] private readonly SharedEmpSystem _emp = default!;
|
||||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
[Dependency] private readonly SpaceNinjaSystem _ninja = default!;
|
||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
@@ -30,7 +30,6 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
||||||
}
|
}
|
||||||
@@ -44,7 +43,7 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
// raise event to let ninja components get starting battery
|
// raise event to let ninja components get starting battery
|
||||||
_ninja.GetNinjaBattery(user.Owner, out var uid, out var _);
|
_ninja.GetNinjaBattery(user.Owner, out var uid, out var _);
|
||||||
|
|
||||||
if (uid is not {} battery_uid)
|
if (uid is not { } battery_uid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new NinjaBatteryChangedEvent(battery_uid, ent.Owner);
|
var ev = new NinjaBatteryChangedEvent(battery_uid, ent.Owner);
|
||||||
@@ -96,17 +95,10 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
// if a cell is able to automatically recharge, boost the score drastically depending on the recharge rate,
|
// if a cell is able to automatically recharge, boost the score drastically depending on the recharge rate,
|
||||||
// this is to ensure a ninja can still upgrade to a micro reactor cell even if they already have a medium or high.
|
// this is to ensure a ninja can still upgrade to a micro reactor cell even if they already have a medium or high.
|
||||||
if (TryComp<BatterySelfRechargerComponent>(uid, out var selfcomp) && selfcomp.AutoRecharge)
|
if (TryComp<BatterySelfRechargerComponent>(uid, out var selfcomp) && selfcomp.AutoRecharge)
|
||||||
return battcomp.MaxCharge + (selfcomp.AutoRechargeRate*AutoRechargeValue);
|
return battcomp.MaxCharge + selfcomp.AutoRechargeRate * AutoRechargeValue;
|
||||||
return battcomp.MaxCharge;
|
return battcomp.MaxCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpAttempt(EntityUid uid, NinjaSuitComponent comp, EmpAttemptEvent args)
|
|
||||||
{
|
|
||||||
// ninja suit (battery) is immune to emp
|
|
||||||
// powercell relays the event to suit
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UserUnequippedSuit(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
protected override void UserUnequippedSuit(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
|
||||||
{
|
{
|
||||||
base.UserUnequippedSuit(ent, user);
|
base.UserUnequippedSuit(ent, user);
|
||||||
@@ -144,6 +136,7 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
Popup.PopupEntity(Loc.GetString(message), user, user);
|
Popup.PopupEntity(Loc.GetString(message), user, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move this to shared when power cells are predicted.
|
||||||
private void OnEmp(Entity<NinjaSuitComponent> ent, ref NinjaEmpEvent args)
|
private void OnEmp(Entity<NinjaSuitComponent> ent, ref NinjaEmpEvent args)
|
||||||
{
|
{
|
||||||
var (uid, comp) = ent;
|
var (uid, comp) = ent;
|
||||||
@@ -159,7 +152,6 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
|||||||
if (CheckDisabled(ent, user))
|
if (CheckDisabled(ent, user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var coords = _transform.GetMapCoordinates(user);
|
_emp.EmpPulse(Transform(user).Coordinates, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration, user);
|
||||||
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
using Content.Server.Communications;
|
using Content.Server.Communications;
|
||||||
using Content.Server.Chat.Managers;
|
|
||||||
using Content.Server.CriminalRecords.Systems;
|
using Content.Server.CriminalRecords.Systems;
|
||||||
using Content.Server.GameTicking.Rules.Components;
|
|
||||||
using Content.Server.Objectives.Components;
|
using Content.Server.Objectives.Components;
|
||||||
using Content.Server.Objectives.Systems;
|
using Content.Server.Objectives.Systems;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Server.Research.Systems;
|
using Content.Server.Research.Systems;
|
||||||
using Content.Server.Roles;
|
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Ninja.Components;
|
using Content.Shared.Ninja.Components;
|
||||||
using Content.Shared.Ninja.Systems;
|
using Content.Shared.Ninja.Systems;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
|
|
||||||
namespace Content.Server.Ninja.Systems;
|
namespace Content.Server.Ninja.Systems;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Power.NodeGroups;
|
using Content.Server.Power.NodeGroups;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
namespace Content.Server.Power.Components
|
namespace Content.Server.Power.Components
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Shared.Guidebook;
|
|
||||||
|
|
||||||
namespace Content.Server.Power.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Battery node on the pow3r network. Needs other components to connect to actual networks.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
[Virtual]
|
|
||||||
[Access(typeof(BatterySystem))]
|
|
||||||
public partial class BatteryComponent : Component
|
|
||||||
{
|
|
||||||
public string SolutionName = "battery";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum charge of the battery in joules (ie. watt seconds)
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
[GuidebookData]
|
|
||||||
public float MaxCharge;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Current charge of the battery in joules (ie. watt seconds)
|
|
||||||
/// </summary>
|
|
||||||
[DataField("startingCharge")]
|
|
||||||
public float CurrentCharge;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The price per one joule. Default is 1 credit for 10kJ.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public float PricePerJoule = 0.0001f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage).
|
|
||||||
/// </summary>
|
|
||||||
[ByRefEvent]
|
|
||||||
public readonly record struct ChargeChangedEvent(float Charge, float MaxCharge);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when it is necessary to get information about battery charges.
|
|
||||||
/// </summary>
|
|
||||||
[ByRefEvent]
|
|
||||||
public sealed class GetChargeEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public float CurrentCharge;
|
|
||||||
public float MaxCharge;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when it is necessary to change the current battery charge to a some value.
|
|
||||||
/// </summary>
|
|
||||||
[ByRefEvent]
|
|
||||||
public sealed class ChangeChargeEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public float OriginalValue;
|
|
||||||
public float ResidualValue;
|
|
||||||
|
|
||||||
public ChangeChargeEvent(float value)
|
|
||||||
{
|
|
||||||
OriginalValue = value;
|
|
||||||
ResidualValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Content.Shared.Power;
|
|
||||||
using Content.Shared.Whitelist;
|
|
||||||
|
|
||||||
namespace Content.Server.Power.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class ChargerComponent : Component
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
public CellChargerStatus Status;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The charge rate of the charger, in watts
|
|
||||||
/// </summary>
|
|
||||||
[DataField("chargeRate")]
|
|
||||||
public float ChargeRate = 20.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The container ID that is holds the entities being charged.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("slotId", required: true)]
|
|
||||||
public string SlotId = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A whitelist for what entities can be charged by this Charger.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("whitelist")]
|
|
||||||
public EntityWhitelist? Whitelist;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the charger is portable and thus subject to EMP effects
|
|
||||||
/// and bypasses checks for transform, anchored, and ApcPowerReceiverComponent.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public bool Portable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.Pow3r;
|
using Content.Server.Power.Pow3r;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.APC;
|
using Content.Shared.APC;
|
||||||
using Content.Shared.Emag.Systems;
|
using Content.Shared.Emag.Systems;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
@@ -203,6 +204,9 @@ public sealed class ApcSystem : EntitySystem
|
|||||||
return ApcExternalPowerState.Good;
|
return ApcExternalPowerState.Good;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This subscription should be in shared.
|
||||||
|
// But I am not moving ApcComponent to shared, this PR already got soaped enough and that component uses several layers of OOP.
|
||||||
|
// At least the EMP visuals won't mispredict, since all APCs also have the BatteryComponent, which also has a EMP effect and is in shared.
|
||||||
private void OnEmpPulse(EntityUid uid, ApcComponent component, ref EmpPulseEvent args)
|
private void OnEmpPulse(EntityUid uid, ApcComponent component, ref EmpPulseEvent args)
|
||||||
{
|
{
|
||||||
if (component.MainBreakerEnabled)
|
if (component.MainBreakerEnabled)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
namespace Content.Server.Power.EntitySystems;
|
namespace Content.Server.Power.EntitySystems;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Cargo;
|
using Content.Shared.Cargo;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -10,7 +12,7 @@ using Robust.Shared.Timing;
|
|||||||
namespace Content.Server.Power.EntitySystems
|
namespace Content.Server.Power.EntitySystems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class BatterySystem : EntitySystem
|
public sealed class BatterySystem : SharedBatterySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
|
||||||
@@ -22,7 +24,6 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
|
SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
|
||||||
SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
|
SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
|
||||||
SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
|
SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
|
||||||
SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
|
SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
|
||||||
SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
|
SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
if (effectiveMax == 0)
|
if (effectiveMax == 0)
|
||||||
effectiveMax = 1;
|
effectiveMax = 1;
|
||||||
var chargeFraction = batteryComponent.CurrentCharge / effectiveMax;
|
var chargeFraction = batteryComponent.CurrentCharge / effectiveMax;
|
||||||
var chargePercentRounded = (int) (chargeFraction * 100);
|
var chargePercentRounded = (int)(chargeFraction * 100);
|
||||||
args.PushMarkup(
|
args.PushMarkup(
|
||||||
Loc.GetString(
|
Loc.GetString(
|
||||||
"examinable-battery-component-examine-detail",
|
"examinable-battery-component-examine-detail",
|
||||||
@@ -108,15 +109,6 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
{
|
{
|
||||||
args.Price += component.CurrentCharge * component.PricePerJoule;
|
args.Price += component.CurrentCharge * component.PricePerJoule;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
UseCharge(uid, args.EnergyConsumption, component);
|
|
||||||
// Apply a cooldown to the entity's self recharge if needed to avoid it immediately self recharging after an EMP.
|
|
||||||
TrySetChargeCooldown(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChangeCharge(Entity<BatteryComponent> entity, ref ChangeChargeEvent args)
|
private void OnChangeCharge(Entity<BatteryComponent> entity, ref ChangeChargeEvent args)
|
||||||
{
|
{
|
||||||
if (args.ResidualValue == 0)
|
if (args.ResidualValue == 0)
|
||||||
@@ -131,7 +123,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
args.MaxCharge += entity.Comp.MaxCharge;
|
args.MaxCharge += entity.Comp.MaxCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
public override float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
{
|
{
|
||||||
if (value <= 0 || !Resolve(uid, ref battery) || battery.CurrentCharge == 0)
|
if (value <= 0 || !Resolve(uid, ref battery) || battery.CurrentCharge == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -139,7 +131,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
return ChangeCharge(uid, -value, battery);
|
return ChangeCharge(uid, -value, battery);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
public override void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref battery))
|
if (!Resolve(uid, ref battery))
|
||||||
return;
|
return;
|
||||||
@@ -174,7 +166,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the current battery charge by some value
|
/// Changes the current battery charge by some value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float ChangeCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
public override float ChangeCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref battery))
|
if (!Resolve(uid, ref battery))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -190,10 +182,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public override void TrySetChargeCooldown(EntityUid uid, float value = -1)
|
||||||
/// Checks if the entity has a self recharge and puts it on cooldown if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public void TrySetChargeCooldown(EntityUid uid, float value = -1)
|
|
||||||
{
|
{
|
||||||
if (!TryComp<BatterySelfRechargerComponent>(uid, out var batteryself))
|
if (!TryComp<BatterySelfRechargerComponent>(uid, out var batteryself))
|
||||||
return;
|
return;
|
||||||
@@ -228,7 +217,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
|
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
public override bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref battery, false) || value > battery.CurrentCharge)
|
if (!Resolve(uid, ref battery, false) || value > battery.CurrentCharge)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.PowerCell;
|
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Server.PowerCell;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Emp;
|
using Content.Shared.Emp;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@@ -15,7 +16,7 @@ using Content.Shared.Whitelist;
|
|||||||
namespace Content.Server.Power.EntitySystems;
|
namespace Content.Server.Power.EntitySystems;
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
internal sealed class ChargerSystem : EntitySystem
|
public sealed class ChargerSystem : SharedChargerSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ContainerSystem _container = default!;
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
@@ -25,6 +26,8 @@ internal sealed class ChargerSystem : EntitySystem
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<ChargerComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<ChargerComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<ChargerComponent, PowerChangedEvent>(OnPowerChanged);
|
SubscribeLocalEvent<ChargerComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
SubscribeLocalEvent<ChargerComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
SubscribeLocalEvent<ChargerComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||||
@@ -32,8 +35,6 @@ internal sealed class ChargerSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<ChargerComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
SubscribeLocalEvent<ChargerComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
||||||
SubscribeLocalEvent<ChargerComponent, InsertIntoEntityStorageAttemptEvent>(OnEntityStorageInsertAttempt);
|
SubscribeLocalEvent<ChargerComponent, InsertIntoEntityStorageAttemptEvent>(OnEntityStorageInsertAttempt);
|
||||||
SubscribeLocalEvent<ChargerComponent, ExaminedEvent>(OnChargerExamine);
|
SubscribeLocalEvent<ChargerComponent, ExaminedEvent>(OnChargerExamine);
|
||||||
|
|
||||||
SubscribeLocalEvent<ChargerComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, ChargerComponent component, ComponentStartup args)
|
private void OnStartup(EntityUid uid, ChargerComponent component, ComponentStartup args)
|
||||||
@@ -46,7 +47,7 @@ internal sealed class ChargerSystem : EntitySystem
|
|||||||
using (args.PushGroup(nameof(ChargerComponent)))
|
using (args.PushGroup(nameof(ChargerComponent)))
|
||||||
{
|
{
|
||||||
// rate at which the charger charges
|
// rate at which the charger charges
|
||||||
args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int) component.ChargeRate)));
|
args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int)component.ChargeRate)));
|
||||||
|
|
||||||
// try to get contents of the charger
|
// try to get contents of the charger
|
||||||
if (!_container.TryGetContainer(uid, component.SlotId, out var container))
|
if (!_container.TryGetContainer(uid, component.SlotId, out var container))
|
||||||
@@ -70,7 +71,7 @@ internal sealed class ChargerSystem : EntitySystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var chargePercentage = (battery.CurrentCharge / battery.MaxCharge) * 100;
|
var chargePercentage = (battery.CurrentCharge / battery.MaxCharge) * 100;
|
||||||
args.PushMarkup(Loc.GetString("charger-content", ("chargePercentage", (int) chargePercentage)));
|
args.PushMarkup(Loc.GetString("charger-content", ("chargePercentage", (int)chargePercentage)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,12 +195,6 @@ internal sealed class ChargerSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, ChargerComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component)
|
private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component)
|
||||||
{
|
{
|
||||||
if (!component.Portable)
|
if (!component.Portable)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Content.Server.NodeContainer;
|
|
||||||
using Content.Server.NodeContainer.EntitySystems;
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
using Content.Server.NodeContainer.Nodes;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.Nodes;
|
using Content.Server.Power.Nodes;
|
||||||
using Content.Server.Power.NodeGroups;
|
using Content.Server.Power.NodeGroups;
|
||||||
@@ -9,6 +7,7 @@ using Content.Shared.GameTicking.Components;
|
|||||||
using Content.Shared.Pinpointer;
|
using Content.Shared.Pinpointer;
|
||||||
using Content.Shared.Station.Components;
|
using Content.Shared.Station.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Server.Kitchen.Components;
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
|
|
||||||
namespace Content.Server.Power.EntitySystems;
|
namespace Content.Server.Power.EntitySystems;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
using Content.Shared.SMES;
|
using Content.Shared.SMES;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
namespace Content.Server.Power
|
namespace Content.Server.Power
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
using Content.Server.Emp;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Containers.ItemSlots;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.PowerCell;
|
using Content.Shared.PowerCell;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
|
using Content.Shared.UserInterface;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Content.Server.Kitchen.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.Containers.ItemSlots;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem;
|
|
||||||
|
|
||||||
namespace Content.Server.PowerCell;
|
namespace Content.Server.PowerCell;
|
||||||
|
|
||||||
@@ -34,7 +34,6 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
|
SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||||
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
||||||
SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(OnCellEmpAttempt);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
||||||
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
||||||
@@ -221,14 +220,6 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
|||||||
OnBatteryExamined(uid, battery, args);
|
OnBatteryExamined(uid, battery, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCellEmpAttempt(EntityUid uid, PowerCellComponent component, EmpAttemptEvent args)
|
|
||||||
{
|
|
||||||
var parent = Transform(uid).ParentUid;
|
|
||||||
// relay the attempt event to the slot so it can cancel it
|
|
||||||
if (HasComp<PowerCellSlotComponent>(parent))
|
|
||||||
RaiseLocalEvent(parent, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args)
|
private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args)
|
||||||
{
|
{
|
||||||
TryGetBatteryFromSlot(uid, out var battery);
|
TryGetBatteryFromSlot(uid, out var battery);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Examine;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
using Content.Server.Chat.Systems;
|
|
||||||
using Content.Server.Station.Systems;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.PowerSink
|
namespace Content.Server.PowerSink
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Radio;
|
using Content.Shared.Radio;
|
||||||
using Content.Shared.Radio.Components;
|
using Content.Shared.Radio.Components;
|
||||||
@@ -22,8 +21,6 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
|||||||
SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged);
|
SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged);
|
||||||
|
|
||||||
SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak);
|
SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak);
|
||||||
|
|
||||||
SubscribeLocalEvent<HeadsetComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args)
|
private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args)
|
||||||
@@ -71,7 +68,6 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
|||||||
protected override void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
protected override void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
base.OnGotUnequipped(uid, component, args);
|
base.OnGotUnequipped(uid, component, args);
|
||||||
component.IsEquipped = false;
|
|
||||||
RemComp<ActiveRadioComponent>(uid);
|
RemComp<ActiveRadioComponent>(uid);
|
||||||
RemComp<WearingHeadsetComponent>(args.Equipee);
|
RemComp<WearingHeadsetComponent>(args.Equipee);
|
||||||
}
|
}
|
||||||
@@ -84,6 +80,9 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
|||||||
if (component.Enabled == value)
|
if (component.Enabled == value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
component.Enabled = value;
|
||||||
|
Dirty(uid, component);
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
RemCompDeferred<ActiveRadioComponent>(uid);
|
RemCompDeferred<ActiveRadioComponent>(uid);
|
||||||
@@ -115,13 +114,4 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
|||||||
if (TryComp(parent, out ActorComponent? actor))
|
if (TryComp(parent, out ActorComponent? actor))
|
||||||
_netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.Channel);
|
_netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.Channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, HeadsetComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
if (component.Enabled)
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
namespace Content.Server.SensorMonitoring;
|
namespace Content.Server.SensorMonitoring;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Content.Server.DeviceNetwork;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
using Content.Server.DeviceNetwork.Systems;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.DeviceNetwork.Events;
|
using Content.Shared.DeviceNetwork.Events;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
namespace Content.Server.SensorMonitoring;
|
namespace Content.Server.SensorMonitoring;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ using Content.Shared.DoAfter;
|
|||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using Content.Shared.Examine;
|
|||||||
using Content.Shared.Item.ItemToggle;
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Item.ItemToggle.Components;
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
|
|
||||||
namespace Content.Server.Stunnable.Systems
|
namespace Content.Server.Stunnable.Systems
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Shared.Speech;
|
using Content.Shared.Speech;
|
||||||
using Content.Shared.Speech.Components;
|
using Content.Shared.Speech.Components;
|
||||||
|
using Content.Shared.SurveillanceCamera.Components;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using static Content.Server.Chat.Systems.ChatSystem;
|
using static Content.Server.Chat.Systems.ChatSystem;
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.DeviceNetwork.Systems;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.DeviceNetwork.Events;
|
using Content.Shared.DeviceNetwork.Events;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.SurveillanceCamera;
|
using Content.Shared.SurveillanceCamera;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.SurveillanceCamera.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -15,7 +14,7 @@ using Content.Shared.DeviceNetwork.Components;
|
|||||||
|
|
||||||
namespace Content.Server.SurveillanceCamera;
|
namespace Content.Server.SurveillanceCamera;
|
||||||
|
|
||||||
public sealed class SurveillanceCameraSystem : EntitySystem
|
public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||||
@@ -57,15 +56,13 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<SurveillanceCameraComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, PowerChangedEvent>(OnPowerChanged);
|
SubscribeLocalEvent<SurveillanceCameraComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
SubscribeLocalEvent<SurveillanceCameraComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetName>(OnSetName);
|
SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetName>(OnSetName);
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetNetwork>(OnSetNetwork);
|
SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetNetwork>(OnSetNetwork);
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, GetVerbsEvent<AlternativeVerb>>(AddVerbs);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
SubscribeLocalEvent<SurveillanceCameraComponent, EmpDisabledRemoved>(OnEmpDisabledRemoved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args)
|
private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args)
|
||||||
@@ -131,26 +128,6 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddVerbs(EntityUid uid, SurveillanceCameraComponent component, GetVerbsEvent<AlternativeVerb> verbs)
|
|
||||||
{
|
|
||||||
if (!_actionBlocker.CanInteract(verbs.User, uid) || !_actionBlocker.CanComplexInteract(verbs.User))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.NameSet && component.NetworkSet)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AlternativeVerb verb = new();
|
|
||||||
verb.Text = Loc.GetString("surveillance-camera-setup");
|
|
||||||
verb.Act = () => OpenSetupInterface(uid, verbs.User, component);
|
|
||||||
verbs.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void OnPowerChanged(EntityUid camera, SurveillanceCameraComponent component, ref PowerChangedEvent args)
|
private void OnPowerChanged(EntityUid camera, SurveillanceCameraComponent component, ref PowerChangedEvent args)
|
||||||
{
|
{
|
||||||
SetActive(camera, args.Powered, component);
|
SetActive(camera, args.Powered, component);
|
||||||
@@ -173,6 +150,7 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
|
|
||||||
component.CameraId = args.Name;
|
component.CameraId = args.Name;
|
||||||
component.NameSet = true;
|
component.NameSet = true;
|
||||||
|
Dirty(uid, component);
|
||||||
UpdateSetupInterface(uid, component);
|
UpdateSetupInterface(uid, component);
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor)} set the name of {ToPrettyString(uid)} to \"{args.Name}.\"");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor)} set the name of {ToPrettyString(uid)} to \"{args.Name}.\"");
|
||||||
}
|
}
|
||||||
@@ -198,10 +176,11 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
|
|
||||||
_deviceNetworkSystem.SetReceiveFrequency(uid, frequency.Frequency);
|
_deviceNetworkSystem.SetReceiveFrequency(uid, frequency.Frequency);
|
||||||
component.NetworkSet = true;
|
component.NetworkSet = true;
|
||||||
|
Dirty(uid, component);
|
||||||
UpdateSetupInterface(uid, component);
|
UpdateSetupInterface(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSetupInterface(EntityUid uid, EntityUid player, SurveillanceCameraComponent? camera = null)
|
protected override void OpenSetupInterface(EntityUid uid, EntityUid player, SurveillanceCameraComponent? camera = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref camera))
|
if (!Resolve(uid, ref camera))
|
||||||
return;
|
return;
|
||||||
@@ -271,7 +250,7 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
UpdateVisuals(camera, component);
|
UpdateVisuals(camera, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetActive(EntityUid camera, bool setting, SurveillanceCameraComponent? component = null)
|
public override void SetActive(EntityUid camera, bool setting, SurveillanceCameraComponent? component = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(camera, ref component))
|
if (!Resolve(camera, ref component))
|
||||||
{
|
{
|
||||||
@@ -418,21 +397,6 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
|||||||
|
|
||||||
_appearance.SetData(uid, SurveillanceCameraVisualsKey.Key, key, appearance);
|
_appearance.SetData(uid, SurveillanceCameraVisualsKey.Key, key, appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, SurveillanceCameraComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
if (component.Active)
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
SetActive(uid, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEmpDisabledRemoved(EntityUid uid, SurveillanceCameraComponent component, ref EmpDisabledRemoved args)
|
|
||||||
{
|
|
||||||
SetActive(uid, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class OnSurveillanceCameraViewerAddEvent : EntityEventArgs
|
public sealed class OnSurveillanceCameraViewerAddEvent : EntityEventArgs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Tesla.Components;
|
using Content.Server.Tesla.Components;
|
||||||
using Content.Server.Lightning;
|
using Content.Server.Lightning;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
namespace Content.Server.Tesla.EntitySystems;
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Cargo.Systems;
|
using Content.Server.Cargo.Systems;
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Vocalization.Systems;
|
using Content.Server.Vocalization.Systems;
|
||||||
using Content.Shared.Cargo;
|
using Content.Shared.Cargo;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -35,7 +33,6 @@ namespace Content.Server.VendingMachines
|
|||||||
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
|
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged);
|
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
|
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
|
SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
||||||
@@ -86,6 +83,7 @@ namespace Content.Server.VendingMachines
|
|||||||
private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, BreakageEventArgs eventArgs)
|
private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, BreakageEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
vendComponent.Broken = true;
|
vendComponent.Broken = true;
|
||||||
|
Dirty(uid, vendComponent);
|
||||||
TryUpdateVisualState((uid, vendComponent));
|
TryUpdateVisualState((uid, vendComponent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +92,7 @@ namespace Content.Server.VendingMachines
|
|||||||
if (!args.DamageIncreased && component.Broken)
|
if (!args.DamageIncreased && component.Broken)
|
||||||
{
|
{
|
||||||
component.Broken = false;
|
component.Broken = false;
|
||||||
|
Dirty(uid, component);
|
||||||
TryUpdateVisualState((uid, component));
|
TryUpdateVisualState((uid, component));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -257,16 +256,6 @@ namespace Content.Server.VendingMachines
|
|||||||
args.Price += priceSets.Max();
|
args.Price += priceSets.Max();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmpPulse(EntityUid uid, VendingMachineComponent component, ref EmpPulseEvent args)
|
|
||||||
{
|
|
||||||
if (!component.Broken && this.IsPowered(uid, EntityManager))
|
|
||||||
{
|
|
||||||
args.Affected = true;
|
|
||||||
args.Disabled = true;
|
|
||||||
component.NextEmpEject = Timing.CurTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTryVocalize(Entity<VendingMachineComponent> ent, ref TryVocalizeEvent args)
|
private void OnTryVocalize(Entity<VendingMachineComponent> ent, ref TryVocalizeEvent args)
|
||||||
{
|
{
|
||||||
args.Cancelled |= ent.Comp.Broken;
|
args.Cancelled |= ent.Comp.Broken;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Events;
|
using Content.Shared.Damage.Events;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Weapons.Ranged;
|
using Content.Shared.Weapons.Ranged;
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ public sealed partial class XAEEmpInAreaComponent : Component
|
|||||||
/// Duration (in seconds) for which devices going to be disabled.
|
/// Duration (in seconds) for which devices going to be disabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public float DisableDuration = 60f;
|
public TimeSpan DisableDuration = TimeSpan.FromSeconds(60);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Xenoarchaeology.Artifact.XAE.Components;
|
using Content.Server.Xenoarchaeology.Artifact.XAE.Components;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
using Content.Shared.Xenoarchaeology.Artifact;
|
using Content.Shared.Xenoarchaeology.Artifact;
|
||||||
using Content.Shared.Xenoarchaeology.Artifact.XAE;
|
using Content.Shared.Xenoarchaeology.Artifact.XAE;
|
||||||
|
|
||||||
|
|||||||
@@ -61,5 +61,5 @@ public sealed partial class ElectricityAnomalyComponent : Component
|
|||||||
/// Duration of devices being disabled by the emp pulse upon going supercritical.
|
/// Duration of devices being disabled by the emp pulse upon going supercritical.
|
||||||
/// <summary>
|
/// <summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float EmpDisabledDuration = 60f;
|
public TimeSpan EmpDisabledDuration = TimeSpan.FromSeconds(60);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ using System.Linq;
|
|||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Contraband;
|
using Content.Shared.Contraband;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Lock;
|
using Content.Shared.Lock;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -23,8 +26,11 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly TagSystem _tag = default!;
|
[Dependency] private readonly TagSystem _tag = default!;
|
||||||
[Dependency] protected readonly IGameTiming _timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
[Dependency] private readonly LockSystem _lock = default!;
|
[Dependency] private readonly LockSystem _lock = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
|
||||||
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
|
|
||||||
private static readonly SlotFlags[] IgnoredSlots =
|
private static readonly SlotFlags[] IgnoredSlots =
|
||||||
{
|
{
|
||||||
@@ -32,12 +38,12 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
SlotFlags.PREVENTEQUIP,
|
SlotFlags.PREVENTEQUIP,
|
||||||
SlotFlags.NONE
|
SlotFlags.NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly SlotFlags[] Slots = Enum.GetValues<SlotFlags>().Except(IgnoredSlots).ToArray();
|
private static readonly SlotFlags[] Slots = Enum.GetValues<SlotFlags>().Except(IgnoredSlots).ToArray();
|
||||||
|
|
||||||
private readonly Dictionary<SlotFlags, List<EntProtoId>> _data = new();
|
private readonly Dictionary<SlotFlags, List<EntProtoId>> _data = new();
|
||||||
|
|
||||||
public readonly Dictionary<SlotFlags, List<string>> ValidVariants = new();
|
public readonly Dictionary<SlotFlags, List<string>> ValidVariants = new();
|
||||||
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
|
|
||||||
|
|
||||||
private static readonly ProtoId<TagPrototype> WhitelistChameleonTag = "WhitelistChameleon";
|
private static readonly ProtoId<TagPrototype> WhitelistChameleonTag = "WhitelistChameleon";
|
||||||
|
|
||||||
@@ -47,6 +53,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<ChameleonClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
SubscribeLocalEvent<ChameleonClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
SubscribeLocalEvent<ChameleonClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, GetVerbsEvent<InteractionVerb>>(OnVerb);
|
SubscribeLocalEvent<ChameleonClothingComponent, GetVerbsEvent<InteractionVerb>>(OnVerb);
|
||||||
|
SubscribeLocalEvent<ChameleonClothingComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
|
|
||||||
SubscribeLocalEvent<ChameleonClothingComponent, PrototypesReloadedEventArgs>(OnPrototypeReload);
|
SubscribeLocalEvent<ChameleonClothingComponent, PrototypesReloadedEventArgs>(OnPrototypeReload);
|
||||||
PrepareAllVariants();
|
PrepareAllVariants();
|
||||||
@@ -97,21 +104,21 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
|
|
||||||
// clothing sprite logic
|
// clothing sprite logic
|
||||||
if (TryComp(uid, out ClothingComponent? clothing) &&
|
if (TryComp(uid, out ClothingComponent? clothing) &&
|
||||||
proto.TryGetComponent("Clothing", out ClothingComponent? otherClothing))
|
proto.TryGetComponent(out ClothingComponent? otherClothing, Factory))
|
||||||
{
|
{
|
||||||
_clothingSystem.CopyVisuals(uid, otherClothing, clothing);
|
_clothingSystem.CopyVisuals(uid, otherClothing, clothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// appearance data logic
|
// appearance data logic
|
||||||
if (TryComp(uid, out AppearanceComponent? appearance) &&
|
if (TryComp(uid, out AppearanceComponent? appearance) &&
|
||||||
proto.TryGetComponent("Appearance", out AppearanceComponent? appearanceOther))
|
proto.TryGetComponent(out AppearanceComponent? appearanceOther, Factory))
|
||||||
{
|
{
|
||||||
_appearance.AppendData(appearanceOther, uid);
|
_appearance.AppendData(appearanceOther, uid);
|
||||||
Dirty(uid, appearance);
|
Dirty(uid, appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// properly mark contraband
|
// properly mark contraband
|
||||||
if (proto.TryGetComponent("Contraband", out ContrabandComponent? contra))
|
if (proto.TryGetComponent(out ContrabandComponent? contra, Factory))
|
||||||
{
|
{
|
||||||
EnsureComp<ContrabandComponent>(uid, out var current);
|
EnsureComp<ContrabandComponent>(uid, out var current);
|
||||||
_contraband.CopyDetails(uid, contra, current);
|
_contraband.CopyDetails(uid, contra, current);
|
||||||
@@ -138,6 +145,24 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(EntityUid uid, ChameleonClothingComponent component, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
if (!component.AffectedByEmp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.EmpContinuous)
|
||||||
|
component.NextEmpChange = Timing.CurTime + TimeSpan.FromSeconds(1f / component.EmpChangeIntensity);
|
||||||
|
|
||||||
|
if (_net.IsServer) // needs RandomPredicted
|
||||||
|
{
|
||||||
|
var pick = GetRandomValidPrototype(component.Slot, component.RequireTag);
|
||||||
|
SetSelectedPrototype(uid, pick, component: component);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void UpdateSprite(EntityUid uid, EntityPrototype proto) { }
|
protected virtual void UpdateSprite(EntityUid uid, EntityPrototype proto) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -157,7 +182,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// check if it's valid clothing
|
// check if it's valid clothing
|
||||||
if (!proto.TryGetComponent("Clothing", out ClothingComponent? clothing))
|
if (!proto.TryGetComponent(out ClothingComponent? clothing, Factory))
|
||||||
return false;
|
return false;
|
||||||
if (!clothing.Slots.HasFlag(chameleonSlot))
|
if (!clothing.Slots.HasFlag(chameleonSlot))
|
||||||
return false;
|
return false;
|
||||||
@@ -187,6 +212,14 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
return validTargets;
|
return validTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a random prototype for a given slot.
|
||||||
|
/// </summary>
|
||||||
|
public string GetRandomValidPrototype(SlotFlags slot, string? tag = null)
|
||||||
|
{
|
||||||
|
return _random.Pick(GetValidTargets(slot, tag).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
protected void PrepareAllVariants()
|
protected void PrepareAllVariants()
|
||||||
{
|
{
|
||||||
_data.Clear();
|
_data.Clear();
|
||||||
@@ -215,4 +248,9 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Predict and use component states for the UI
|
||||||
|
public virtual void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false,
|
||||||
|
ChameleonClothingComponent? component = null)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,24 +5,30 @@ namespace Content.Shared.Emp;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// While entity has this component it is "disabled" by EMP.
|
/// While entity has this component it is "disabled" by EMP.
|
||||||
/// Add desired behaviour in other systems
|
/// Add desired behaviour in other systems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||||
[Access(typeof(SharedEmpSystem))]
|
[Access(typeof(SharedEmpSystem))]
|
||||||
public sealed partial class EmpDisabledComponent : Component
|
public sealed partial class EmpDisabledComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moment of time when component is removed and entity stops being "disabled"
|
/// Moment of time when the component is removed and entity stops being "disabled".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("timeLeft", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
[AutoPausedField]
|
[AutoNetworkedField, AutoPausedField]
|
||||||
public TimeSpan DisabledUntil;
|
public TimeSpan DisabledUntil = TimeSpan.Zero;
|
||||||
|
|
||||||
[DataField("effectCoolDown"), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float EffectCooldown = 3f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When next effect will be spawned
|
/// Default time between visual effect spawns.
|
||||||
|
/// This gets a random multiplier.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public TimeSpan EffectCooldown = TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When next effect will be spawned.
|
||||||
|
/// TODO: Particle system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoPausedField]
|
[AutoPausedField]
|
||||||
public TimeSpan TargetTime = TimeSpan.Zero;
|
public TimeSpan TargetTime = TimeSpan.Zero;
|
||||||
|
|||||||
@@ -1,21 +1,58 @@
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Rejuvenate;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Emp;
|
namespace Content.Shared.Emp;
|
||||||
|
|
||||||
public abstract class SharedEmpSystem : EntitySystem
|
public abstract class SharedEmpSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
|
private HashSet<EntityUid> _entSet = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<EmpDisabledComponent, ExaminedEvent>(OnExamine);
|
SubscribeLocalEvent<EmpDisabledComponent, ExaminedEvent>(OnExamine);
|
||||||
|
SubscribeLocalEvent<EmpDisabledComponent, ComponentRemove>(OnRemove);
|
||||||
|
SubscribeLocalEvent<EmpDisabledComponent, RejuvenateEvent>(OnRejuvenate);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected const string EmpDisabledEffectPrototype = "EffectEmpDisabled";
|
public static readonly EntProtoId EmpPulseEffectPrototype = "EffectEmpPulse";
|
||||||
|
public static readonly EntProtoId EmpDisabledEffectPrototype = "EffectEmpDisabled";
|
||||||
|
public static readonly SoundSpecifier EmpSound = new SoundPathSpecifier("/Audio/Effects/Lightning/lightningbolt.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then by raising <see cref="EmpPulseEvent"/> on all entities in range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="coordinates">The location to trigger the EMP pulse at.</param>
|
||||||
|
/// <param name="range">The range of the EMP pulse.</param>
|
||||||
|
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse. In Joule.</param>
|
||||||
|
/// <param name="duration">The duration of the EMP effects.</param>
|
||||||
|
/// <param name="user">The player that caused the effect. Used for predicted audio.</param>
|
||||||
|
public void EmpPulse(MapCoordinates mapCoordinates, float range, float energyConsumption, TimeSpan duration, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
foreach (var uid in _lookup.GetEntitiesInRange(mapCoordinates, range))
|
||||||
|
{
|
||||||
|
TryEmpEffects(uid, energyConsumption, duration, user);
|
||||||
|
}
|
||||||
|
// TODO: replace with PredictedSpawn once it works with animated sprites
|
||||||
|
if (_net.IsServer)
|
||||||
|
Spawn(EmpPulseEffectPrototype, mapCoordinates);
|
||||||
|
|
||||||
|
var coordinates = _transform.ToCoordinates(mapCoordinates);
|
||||||
|
_audio.PlayPredicted(EmpSound, coordinates, user);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
|
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
|
||||||
@@ -24,12 +61,119 @@ public abstract class SharedEmpSystem : EntitySystem
|
|||||||
/// <param name="range">The range of the EMP pulse.</param>
|
/// <param name="range">The range of the EMP pulse.</param>
|
||||||
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
|
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
|
||||||
/// <param name="duration">The duration of the EMP effects.</param>
|
/// <param name="duration">The duration of the EMP effects.</param>
|
||||||
public virtual void EmpPulse(MapCoordinates coordinates, float range, float energyConsumption, float duration)
|
/// <param name="user">The player that caused the effect. Used for predicted audio.</param>
|
||||||
|
public void EmpPulse(EntityCoordinates coordinates, float range, float energyConsumption, TimeSpan duration, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
|
_entSet.Clear();
|
||||||
|
_lookup.GetEntitiesInRange(coordinates, range, _entSet);
|
||||||
|
foreach (var uid in _entSet)
|
||||||
|
{
|
||||||
|
TryEmpEffects(uid, energyConsumption, duration, user);
|
||||||
|
}
|
||||||
|
// TODO: replace with PredictedSpawn once it works with animated sprites
|
||||||
|
if (_net.IsServer)
|
||||||
|
Spawn(EmpPulseEffectPrototype, coordinates);
|
||||||
|
|
||||||
|
_audio.PlayPredicted(EmpSound, coordinates, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to apply the effects of an EMP pulse onto an entity by first raising an <see cref="EmpAttemptEvent"/>, followed by raising a <see cref="EmpPulseEvent"/> on it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The entity to apply the EMP effects on.</param>
|
||||||
|
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
|
||||||
|
/// <param name="duration">The duration of the EMP effects.</param>
|
||||||
|
/// <param name="user">The player that caused the EMP. For prediction purposes.</param>
|
||||||
|
/// <returns>If the entity was affected by the EMP.</returns>
|
||||||
|
public bool TryEmpEffects(EntityUid uid, float energyConsumption, TimeSpan duration, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
var attemptEv = new EmpAttemptEvent();
|
||||||
|
RaiseLocalEvent(uid, ref attemptEv);
|
||||||
|
if (attemptEv.Cancelled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return DoEmpEffects(uid, energyConsumption, duration, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the effects of an EMP pulse onto an entity by raising a <see cref="EmpPulseEvent"/> on it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The entity to apply the EMP effects on.</param>
|
||||||
|
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
|
||||||
|
/// <param name="duration">The duration of the EMP effects.</param>
|
||||||
|
/// <param name="user">The player that caused the EMP. For prediction purposes.</param>
|
||||||
|
/// <returns>If the entity was affected by the EMP.</returns>
|
||||||
|
public bool DoEmpEffects(EntityUid uid, float energyConsumption, TimeSpan duration, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
var ev = new EmpPulseEvent(energyConsumption, false, false, duration, user);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
|
// TODO: replace with PredictedSpawn once it works with animated sprites
|
||||||
|
if (ev.Affected && _net.IsServer)
|
||||||
|
Spawn(EmpDisabledEffectPrototype, Transform(uid).Coordinates);
|
||||||
|
|
||||||
|
if (!ev.Disabled)
|
||||||
|
return ev.Affected;
|
||||||
|
|
||||||
|
var disabled = EnsureComp<EmpDisabledComponent>(uid);
|
||||||
|
disabled.DisabledUntil = Timing.CurTime + duration;
|
||||||
|
Dirty(uid, disabled);
|
||||||
|
|
||||||
|
return ev.Affected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var curTime = Timing.CurTime;
|
||||||
|
var query = EntityQueryEnumerator<EmpDisabledComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp))
|
||||||
|
{
|
||||||
|
if (curTime < comp.DisabledUntil)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
RemComp<EmpDisabledComponent>(uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExamine(Entity<EmpDisabledComponent> ent, ref ExaminedEvent args)
|
private void OnExamine(Entity<EmpDisabledComponent> ent, ref ExaminedEvent args)
|
||||||
{
|
{
|
||||||
args.PushMarkup(Loc.GetString("emp-disabled-comp-on-examine"));
|
args.PushMarkup(Loc.GetString("emp-disabled-comp-on-examine"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRemove(Entity<EmpDisabledComponent> ent, ref ComponentRemove args)
|
||||||
|
{
|
||||||
|
var ev = new EmpDisabledRemovedEvent();
|
||||||
|
RaiseLocalEvent(ent, ref ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRejuvenate(Entity<EmpDisabledComponent> ent, ref RejuvenateEvent args)
|
||||||
|
{
|
||||||
|
RemCompDeferred<EmpDisabledComponent>(ent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on an entity before <see cref="EmpPulseEvent"/>. Cancel this to prevent the emp event being raised.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct EmpAttemptEvent(bool Cancelled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on an entity when it gets hit by an EMP Pulse.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="EnergyConsumption">The amount of energy to remove from batteries. In Joule.</param>
|
||||||
|
/// <param name="Affected">Set this is true in the subscription to spawn a visual effect at the entity's location.</param>
|
||||||
|
/// <param name="Disabled">Set this to ture in the subscription to add <see cref="EmpDisabledComponent"/> to the entity.</param>
|
||||||
|
/// <param name="Duration">The duration the entity will be disabled.</param>
|
||||||
|
/// <param name="User">The player that caused the EMP. For prediction purposes.</param>
|
||||||
|
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct EmpPulseEvent(float EnergyConsumption, bool Affected, bool Disabled, TimeSpan Duration, EntityUid? User);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on an entity after <see cref="EmpDisabledComponent"/> is removed.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct EmpDisabledRemovedEvent();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public sealed partial class EmpReactionEffect : EventEntityEffect<EmpReactionEff
|
|||||||
/// Amount of time entities will be disabled
|
/// Amount of time entities will be disabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("duration")]
|
[DataField("duration")]
|
||||||
public float DisableDuration = 15;
|
public TimeSpan DisableDuration = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||||
=> Loc.GetString("reagent-effect-guidebook-emp-reaction-effect", ("chance", Probability));
|
=> Loc.GetString("reagent-effect-guidebook-emp-reaction-effect", ("chance", Probability));
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ namespace Content.Shared.Input
|
|||||||
public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu";
|
public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu";
|
||||||
public static readonly BoundKeyFunction SmartEquipBackpack = "SmartEquipBackpack";
|
public static readonly BoundKeyFunction SmartEquipBackpack = "SmartEquipBackpack";
|
||||||
public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
|
public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
|
||||||
|
public static readonly BoundKeyFunction SmartEquipPocket1 = "SmartEquipPocket1";
|
||||||
|
public static readonly BoundKeyFunction SmartEquipPocket2 = "SmartEquipPocket2";
|
||||||
|
public static readonly BoundKeyFunction SmartEquipSuitStorage = "SmartEquipSuitStorage";
|
||||||
public static readonly BoundKeyFunction OpenBackpack = "OpenBackpack";
|
public static readonly BoundKeyFunction OpenBackpack = "OpenBackpack";
|
||||||
public static readonly BoundKeyFunction OpenBelt = "OpenBelt";
|
public static readonly BoundKeyFunction OpenBelt = "OpenBelt";
|
||||||
public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
|
public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack, handle: false, outsidePrediction: false))
|
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack, handle: false, outsidePrediction: false))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt, handle: false, outsidePrediction: false))
|
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt, handle: false, outsidePrediction: false))
|
||||||
|
.Bind(ContentKeyFunctions.SmartEquipPocket1, InputCmdHandler.FromDelegate(HandleSmartEquipPocket1, handle: false, outsidePrediction: false))
|
||||||
|
.Bind(ContentKeyFunctions.SmartEquipPocket2, InputCmdHandler.FromDelegate(HandleSmartEquipPocket2, handle: false, outsidePrediction: false))
|
||||||
|
.Bind(ContentKeyFunctions.SmartEquipSuitStorage, InputCmdHandler.FromDelegate(HandleSmartEquipSuitStorage, handle: false, outsidePrediction: false))
|
||||||
.Register<SmartEquipSystem>();
|
.Register<SmartEquipSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +58,21 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
HandleSmartEquip(session, "belt");
|
HandleSmartEquip(session, "belt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleSmartEquipPocket1(ICommonSession? session)
|
||||||
|
{
|
||||||
|
HandleSmartEquip(session, "pocket1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleSmartEquipPocket2(ICommonSession? session)
|
||||||
|
{
|
||||||
|
HandleSmartEquip(session, "pocket2");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleSmartEquipSuitStorage(ICommonSession? session)
|
||||||
|
{
|
||||||
|
HandleSmartEquip(session, "suitstorage");
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleSmartEquip(ICommonSession? session, string equipmentSlot)
|
private void HandleSmartEquip(ICommonSession? session, string equipmentSlot)
|
||||||
{
|
{
|
||||||
if (session is not { } playerSession)
|
if (session is not { } playerSession)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Shared.DeviceLinking.Events;
|
|||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.DeviceNetwork.Events;
|
using Content.Shared.DeviceNetwork.Events;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Light.Components;
|
using Content.Shared.Light.Components;
|
||||||
@@ -52,6 +53,7 @@ public abstract class SharedPoweredLightSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
|
SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
SubscribeLocalEvent<PoweredLightComponent, PoweredLightDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<PoweredLightComponent, PoweredLightDoAfterEvent>(OnDoAfter);
|
||||||
SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
|
SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
|
||||||
|
SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
|
private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
|
||||||
@@ -230,29 +232,21 @@ public abstract class SharedPoweredLightSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try to break bulb inside light fixture
|
/// Try to break bulb inside light fixture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null)
|
public bool TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref light, false))
|
if (!Resolve(uid, ref light, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if we aren't mapinited,
|
|
||||||
// just null the spawned bulb
|
|
||||||
if (LifeStage(uid) < EntityLifeStage.MapInitialized)
|
|
||||||
{
|
|
||||||
light.HasLampOnSpawn = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check bulb state
|
// check bulb state
|
||||||
var bulbUid = GetBulb(uid, light);
|
var bulbUid = GetBulb(uid, light);
|
||||||
if (bulbUid == null || !EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb))
|
if (bulbUid == null || !TryComp<LightBulbComponent>(bulbUid.Value, out var lightBulb))
|
||||||
return false;
|
return false;
|
||||||
if (lightBulb.State == LightBulbState.Broken)
|
if (lightBulb.State == LightBulbState.Broken)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// break it
|
// break it
|
||||||
_bulbSystem.SetState(bulbUid.Value, LightBulbState.Broken, lightBulb);
|
_bulbSystem.SetState(bulbUid.Value, LightBulbState.Broken, lightBulb);
|
||||||
_bulbSystem.PlayBreakSound(bulbUid.Value, lightBulb);
|
_bulbSystem.PlayBreakSound(bulbUid.Value, lightBulb, user);
|
||||||
UpdateLight(uid, light);
|
UpdateLight(uid, light);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -327,13 +321,18 @@ public abstract class SharedPoweredLightSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Destroy the light bulb if the light took any damage.
|
/// Destroy the light bulb if the light took any damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// TODO: This should be an IThresholdBehaviour once DestructibleSystem is predicted.
|
||||||
|
/// </remarks>
|
||||||
public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args)
|
public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args)
|
||||||
{
|
{
|
||||||
|
if (GameTiming.ApplyingState) // The destruction is already networked on its own.
|
||||||
|
return;
|
||||||
|
|
||||||
// Was it being repaired, or did it take damage?
|
// Was it being repaired, or did it take damage?
|
||||||
if (args.DamageIncreased)
|
if (args.DamageIncreased)
|
||||||
{
|
{
|
||||||
// Eventually, this logic should all be done by this (or some other) system, not a component.
|
TryDestroyBulb(uid, component, args.Origin);
|
||||||
TryDestroyBulb(uid, component);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +347,12 @@ public abstract class SharedPoweredLightSystem : EntitySystem
|
|||||||
UpdateLight(uid, component);
|
UpdateLight(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
if (TryDestroyBulb(uid, component))
|
||||||
|
args.Affected = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void ToggleBlinkingLight(EntityUid uid, PoweredLightComponent light, bool isNowBlinking)
|
public void ToggleBlinkingLight(EntityUid uid, PoweredLightComponent light, bool isNowBlinking)
|
||||||
{
|
{
|
||||||
if (light.IsBlinking == isNowBlinking)
|
if (light.IsBlinking == isNowBlinking)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Content.Shared.Clothing;
|
|||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
@@ -53,6 +54,8 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
|
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawn);
|
||||||
SubscribeLocalEvent<SuitSensorComponent, ClothingGotEquippedEvent>(OnEquipped);
|
SubscribeLocalEvent<SuitSensorComponent, ClothingGotEquippedEvent>(OnEquipped);
|
||||||
SubscribeLocalEvent<SuitSensorComponent, ClothingGotUnequippedEvent>(OnUnequipped);
|
SubscribeLocalEvent<SuitSensorComponent, ClothingGotUnequippedEvent>(OnUnequipped);
|
||||||
|
SubscribeLocalEvent<SuitSensorComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
|
SubscribeLocalEvent<SuitSensorComponent, EmpDisabledRemovedEvent>(OnEmpFinished);
|
||||||
SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
|
SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
|
||||||
SubscribeLocalEvent<SuitSensorComponent, GetVerbsEvent<Verb>>(OnVerb);
|
SubscribeLocalEvent<SuitSensorComponent, GetVerbsEvent<Verb>>(OnVerb);
|
||||||
SubscribeLocalEvent<SuitSensorComponent, EntGotInsertedIntoContainerMessage>(OnInsert);
|
SubscribeLocalEvent<SuitSensorComponent, EntGotInsertedIntoContainerMessage>(OnInsert);
|
||||||
@@ -137,6 +140,25 @@ public abstract class SharedSuitSensorSystem : EntitySystem
|
|||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(Entity<SuitSensorComponent> ent, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
|
||||||
|
ent.Comp.PreviousMode = ent.Comp.Mode;
|
||||||
|
SetSensor(ent.AsNullable(), SuitSensorMode.SensorOff, null);
|
||||||
|
|
||||||
|
ent.Comp.PreviousControlsLocked = ent.Comp.ControlsLocked;
|
||||||
|
ent.Comp.ControlsLocked = true;
|
||||||
|
// SetSensor already calls Dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpFinished(Entity<SuitSensorComponent> ent, ref EmpDisabledRemovedEvent args)
|
||||||
|
{
|
||||||
|
SetSensor(ent.AsNullable(), ent.Comp.PreviousMode, null);
|
||||||
|
ent.Comp.ControlsLocked = ent.Comp.PreviousControlsLocked;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnExamine(Entity<SuitSensorComponent> ent, ref ExaminedEvent args)
|
private void OnExamine(Entity<SuitSensorComponent> ent, ref ExaminedEvent args)
|
||||||
{
|
{
|
||||||
if (!args.IsInDetailsRange)
|
if (!args.IsInDetailsRange)
|
||||||
|
|||||||
@@ -85,13 +85,13 @@ public sealed partial class SuitSensorComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The previous mode of the suit. This is used to restore the state when an EMP effect ends.
|
/// The previous mode of the suit. This is used to restore the state when an EMP effect ends.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables]
|
[DataField, AutoNetworkedField, ViewVariables]
|
||||||
public SuitSensorMode PreviousMode = SuitSensorMode.SensorOff;
|
public SuitSensorMode PreviousMode = SuitSensorMode.SensorOff;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The previous locked status of the controls. This is used to restore the state when an EMP effect ends.
|
/// The previous locked status of the controls. This is used to restore the state when an EMP effect ends.
|
||||||
/// This keeps prisoner jumpsuits/internal implants from becoming unlocked after an EMP.
|
/// This keeps prisoner jumpsuits/internal implants from becoming unlocked after an EMP.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables]
|
[DataField, AutoNetworkedField, ViewVariables]
|
||||||
public bool PreviousControlsLocked = false;
|
public bool PreviousControlsLocked = false;
|
||||||
}
|
}
|
||||||
|
|||||||
140
Content.Shared/Medical/VomitSystem.cs
Normal file
140
Content.Shared/Medical/VomitSystem.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Content.Shared.Fluids;
|
||||||
|
using Content.Shared.Forensics.Systems;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Content.Shared.Nutrition.Components;
|
||||||
|
using Content.Shared.Nutrition.EntitySystems;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Medical;
|
||||||
|
|
||||||
|
public sealed class VomitSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||||
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
|
[Dependency] private readonly MovementModStatusSystem _movementMod = default!;
|
||||||
|
[Dependency] private readonly ThirstSystem _thirst = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedBloodstreamSystem _bloodstream = default!;
|
||||||
|
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||||
|
[Dependency] private readonly SharedForensicsSystem _forensics = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly SharedPuddleSystem _puddle = default!;
|
||||||
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BodyComponent, TryVomitEvent>(TryBodyVomitSolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float ChemMultiplier = 0.1f;
|
||||||
|
|
||||||
|
private static readonly ProtoId<SoundCollectionPrototype> VomitCollection = "Vomit";
|
||||||
|
|
||||||
|
private static readonly ProtoId<ReagentPrototype> VomitPrototype = "Vomit"; // TODO: Dehardcode vomit prototype
|
||||||
|
|
||||||
|
private readonly SoundSpecifier _vomitSound = new SoundCollectionSpecifier(VomitCollection,
|
||||||
|
AudioParams.Default.WithVariation(0.2f).WithVolume(-4f));
|
||||||
|
|
||||||
|
private void TryBodyVomitSolution(Entity<BodyComponent> ent, ref TryVomitEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Main requirement: You have a stomach
|
||||||
|
var stomachList = _body.GetBodyOrganEntityComps<StomachComponent>((ent, null));
|
||||||
|
if (stomachList.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Empty the stomach out into it
|
||||||
|
foreach (var stomach in stomachList)
|
||||||
|
{
|
||||||
|
if (_solutionContainer.ResolveSolution(stomach.Owner, StomachSystem.DefaultSolutionName, ref stomach.Comp1.Solution, out var sol))
|
||||||
|
_solutionContainer.TryTransferSolution(stomach.Comp1.Solution.Value, args.Sol, sol.AvailableVolume);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make an entity vomit, if they have a stomach.
|
||||||
|
/// </summary>
|
||||||
|
public void Vomit(EntityUid uid, float thirstAdded = -40f, float hungerAdded = -40f, bool force = false)
|
||||||
|
{
|
||||||
|
// Vomit only if entity is alive
|
||||||
|
// Ignore condition if force was set to true
|
||||||
|
if (!force && _mobState.IsDead(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Need decals
|
||||||
|
var solution = new Solution();
|
||||||
|
|
||||||
|
var ev = new TryVomitEvent(solution, force);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
|
if (!ev.Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Vomiting makes you hungrier and thirstier
|
||||||
|
if (TryComp<HungerComponent>(uid, out var hunger))
|
||||||
|
_hunger.ModifyHunger(uid, hungerAdded, hunger);
|
||||||
|
|
||||||
|
if (TryComp<ThirstComponent>(uid, out var thirst))
|
||||||
|
_thirst.ModifyThirst(uid, thirst, thirstAdded);
|
||||||
|
|
||||||
|
// It fully empties the stomach, this amount from the chem stream is relatively small
|
||||||
|
var solutionSize = (MathF.Abs(thirstAdded) + MathF.Abs(hungerAdded)) / 6;
|
||||||
|
|
||||||
|
// Apply a bit of slowdown
|
||||||
|
_movementMod.TryUpdateMovementSpeedModDuration(uid, MovementModStatusSystem.VomitingSlowdown, TimeSpan.FromSeconds(solutionSize), 0.5f);
|
||||||
|
|
||||||
|
// Adds a tiny amount of the chem stream from earlier along with vomit
|
||||||
|
if (TryComp<BloodstreamComponent>(uid, out var bloodStream))
|
||||||
|
{
|
||||||
|
var vomitAmount = solutionSize;
|
||||||
|
|
||||||
|
// Takes 10% of the chemicals removed from the chem stream
|
||||||
|
if (_solutionContainer.ResolveSolution(uid, bloodStream.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
|
||||||
|
{
|
||||||
|
var vomitChemstreamAmount = _solutionContainer.SplitSolution(bloodStream.ChemicalSolution.Value, vomitAmount);
|
||||||
|
vomitChemstreamAmount.ScaleSolution(ChemMultiplier);
|
||||||
|
solution.AddSolution(vomitChemstreamAmount, _proto);
|
||||||
|
|
||||||
|
vomitAmount -= (float)vomitChemstreamAmount.Volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
|
||||||
|
solution.AddReagent(new ReagentId(VomitPrototype, _bloodstream.GetEntityBloodData(uid)), vomitAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_puddle.TrySpillAt(uid, solution, out var puddle, false))
|
||||||
|
{
|
||||||
|
_forensics.TransferDna(puddle, uid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!_netManager.IsServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Force sound to play as spill doesn't work if solution is empty.
|
||||||
|
_audio.PlayPvs(_vomitSound, uid);
|
||||||
|
_popup.PopupEntity(Loc.GetString("disease-vomit", ("person", Identity.Entity(uid, EntityManager))), uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct TryVomitEvent(Solution Sol, bool Forced = false, bool Handled = false);
|
||||||
@@ -72,10 +72,10 @@ public sealed partial class NinjaSuitComponent : Component
|
|||||||
public float EmpConsumption = 100000f;
|
public float EmpConsumption = 100000f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long the EMP effects last for, in seconds
|
/// How long the EMP effects last for
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public float EmpDuration = 60f;
|
public TimeSpan EmpDuration = TimeSpan.FromSeconds(60);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class RecallKatanaEvent : InstantActionEvent;
|
public sealed partial class RecallKatanaEvent : InstantActionEvent;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
using Content.Shared.Clothing.Components;
|
using Content.Shared.Clothing.Components;
|
||||||
using Content.Shared.Clothing.EntitySystems;
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Item.ItemToggle;
|
using Content.Shared.Item.ItemToggle;
|
||||||
using Content.Shared.Item.ItemToggle.Components;
|
using Content.Shared.Item.ItemToggle.Components;
|
||||||
@@ -18,8 +18,8 @@ namespace Content.Shared.Ninja.Systems;
|
|||||||
public abstract class SharedNinjaSuitSystem : EntitySystem
|
public abstract class SharedNinjaSuitSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
[Dependency] protected readonly SharedPopupSystem Popup = default!;
|
||||||
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
|
[Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!;
|
||||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
@@ -36,6 +36,7 @@ public abstract class SharedNinjaSuitSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<NinjaSuitComponent, CreateItemAttemptEvent>(OnCreateStarAttempt);
|
SubscribeLocalEvent<NinjaSuitComponent, CreateItemAttemptEvent>(OnCreateStarAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
SubscribeLocalEvent<NinjaSuitComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
||||||
SubscribeLocalEvent<NinjaSuitComponent, GotUnequippedEvent>(OnUnequipped);
|
SubscribeLocalEvent<NinjaSuitComponent, GotUnequippedEvent>(OnUnequipped);
|
||||||
|
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEquipped(Entity<NinjaSuitComponent> ent, ref ClothingGotEquippedEvent args)
|
private void OnEquipped(Entity<NinjaSuitComponent> ent, ref ClothingGotEquippedEvent args)
|
||||||
@@ -168,7 +169,14 @@ public abstract class SharedNinjaSuitSystem : EntitySystem
|
|||||||
// mark the user as not wearing a suit
|
// mark the user as not wearing a suit
|
||||||
_ninja.AssignSuit(user, null);
|
_ninja.AssignSuit(user, null);
|
||||||
// disable glove abilities
|
// disable glove abilities
|
||||||
if (user.Comp.Gloves is {} uid)
|
if (user.Comp.Gloves is { } uid)
|
||||||
_toggle.TryDeactivate(uid, user: user);
|
_toggle.TryDeactivate(uid, user: user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmpAttempt(Entity<NinjaSuitComponent> ent, ref EmpAttemptEvent args)
|
||||||
|
{
|
||||||
|
// ninja suit (battery) is immune to emp
|
||||||
|
// powercell relays the event to suit
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
Content.Shared/Power/ChargeEvents.cs
Normal file
33
Content.Shared/Power/ChargeEvents.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace Content.Shared.Power;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage).
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly record struct ChargeChangedEvent(float Charge, float MaxCharge);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when it is necessary to get information about battery charges.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public sealed class GetChargeEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public float CurrentCharge;
|
||||||
|
public float MaxCharge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when it is necessary to change the current battery charge to a some value.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public sealed class ChangeChargeEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public float OriginalValue;
|
||||||
|
public float ResidualValue;
|
||||||
|
|
||||||
|
public ChangeChargeEvent(float value)
|
||||||
|
{
|
||||||
|
OriginalValue = value;
|
||||||
|
ResidualValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace Content.Shared.Power.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attached to APC powered entities that possess a rechargeable internal battery.
|
/// Attached to APC powered entities that possess a rechargeable internal battery.
|
||||||
/// If external power is interrupted, the entity will draw power from this battery instead.
|
/// If external power is interrupted, the entity will draw power from this battery instead.
|
||||||
/// Requires <see cref="Content.Server.Power.Components.ApcPowerReceiverComponent"/> and <see cref="Content.Server.Power.Components.BatteryComponent"/> to function.
|
/// Requires <see cref="Content.Server.Power.Components.ApcPowerReceiverComponent"/> and <see cref="BatteryComponent"/> to function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[Access(typeof(SharedPowerNetSystem), typeof(SharedPowerReceiverSystem))]
|
[Access(typeof(SharedPowerNetSystem), typeof(SharedPowerReceiverSystem))]
|
||||||
|
|||||||
34
Content.Shared/Power/Components/BatteryComponent.cs
Normal file
34
Content.Shared/Power/Components/BatteryComponent.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
|
using Content.Shared.Guidebook;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Battery node on the pow3r network. Needs other components to connect to actual networks.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Virtual]
|
||||||
|
[Access(typeof(SharedBatterySystem))]
|
||||||
|
public partial class BatteryComponent : Component
|
||||||
|
{
|
||||||
|
public string SolutionName = "battery";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum charge of the battery in joules (ie. watt seconds)
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
[GuidebookData]
|
||||||
|
public float MaxCharge;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current charge of the battery in joules (ie. watt seconds)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("startingCharge")]
|
||||||
|
public float CurrentCharge;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The price per one joule. Default is 1 credit for 10kJ.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float PricePerJoule = 0.0001f;
|
||||||
|
}
|
||||||
36
Content.Shared/Power/Components/ChargerComponent.cs
Normal file
36
Content.Shared/Power/Components/ChargerComponent.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class ChargerComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
public CellChargerStatus Status;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The charge rate of the charger, in watts
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float ChargeRate = 20.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The container ID that is holds the entities being charged.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string SlotId = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A whitelist for what entities can be charged by this Charger.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist? Whitelist;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the charger is portable and thus subject to EMP effects
|
||||||
|
/// and bypasses checks for transform, anchored, and ApcPowerReceiverComponent.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Portable = false;
|
||||||
|
}
|
||||||
44
Content.Shared/Power/EntitySystems/SharedBatterySystem.cs
Normal file
44
Content.Shared/Power/EntitySystems/SharedBatterySystem.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Content.Shared.Emp;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power.EntitySystems;
|
||||||
|
|
||||||
|
public abstract class SharedBatterySystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
UseCharge(uid, args.EnergyConsumption, component);
|
||||||
|
// Apply a cooldown to the entity's self recharge if needed to avoid it immediately self recharging after an EMP.
|
||||||
|
TrySetChargeCooldown(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null) { }
|
||||||
|
|
||||||
|
public virtual float ChangeCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the entity has a self recharge and puts it on cooldown if applicable.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void TrySetChargeCooldown(EntityUid uid, float value = -1) { }
|
||||||
|
|
||||||
|
public virtual bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Content.Shared/Power/EntitySystems/SharedChargerSystem.cs
Normal file
20
Content.Shared/Power/EntitySystems/SharedChargerSystem.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Shared.Emp;
|
||||||
|
using Content.Shared.Power.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power.EntitySystems;
|
||||||
|
|
||||||
|
public abstract class SharedChargerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ChargerComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(EntityUid uid, ChargerComponent component, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -22,6 +23,8 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellInserted);
|
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellInserted);
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellRemoved);
|
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellRemoved);
|
||||||
SubscribeLocalEvent<PowerCellSlotComponent, ContainerIsInsertingAttemptEvent>(OnCellInsertAttempt);
|
SubscribeLocalEvent<PowerCellSlotComponent, ContainerIsInsertingAttemptEvent>(OnCellInsertAttempt);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(OnCellEmpAttempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(Entity<PowerCellDrawComponent> ent, ref MapInitEvent args)
|
private void OnMapInit(Entity<PowerCellDrawComponent> ent, ref MapInitEvent args)
|
||||||
@@ -71,6 +74,14 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
|||||||
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnCellEmpAttempt(EntityUid uid, PowerCellComponent component, EmpAttemptEvent args)
|
||||||
|
{
|
||||||
|
var parent = Transform(uid).ParentUid;
|
||||||
|
// relay the attempt event to the slot so it can cancel it
|
||||||
|
if (HasComp<PowerCellSlotComponent>(parent))
|
||||||
|
RaiseLocalEvent(parent, ref args);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDrawEnabled(Entity<PowerCellDrawComponent?> ent, bool enabled)
|
public void SetDrawEnabled(Entity<PowerCellDrawComponent?> ent, bool enabled)
|
||||||
{
|
{
|
||||||
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled)
|
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled)
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Radio.Components;
|
namespace Content.Shared.Radio.Components;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This component relays radio messages to the parent entity's chat when equipped.
|
/// This component relays radio messages to the parent entity's chat when equipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class HeadsetComponent : Component
|
public sealed partial class HeadsetComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("enabled")]
|
[DataField, AutoNetworkedField]
|
||||||
public bool Enabled = true;
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
public bool IsEquipped = false;
|
public bool IsEquipped = false;
|
||||||
|
|
||||||
[DataField("requiredSlot")]
|
[DataField, AutoNetworkedField]
|
||||||
public SlotFlags RequiredSlot = SlotFlags.EARS;
|
public SlotFlags RequiredSlot = SlotFlags.EARS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Radio.Components;
|
using Content.Shared.Radio.Components;
|
||||||
@@ -9,9 +10,11 @@ public abstract class SharedHeadsetSystem : EntitySystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<HeadsetComponent, InventoryRelayedEvent<GetDefaultRadioChannelEvent>>(OnGetDefault);
|
SubscribeLocalEvent<HeadsetComponent, InventoryRelayedEvent<GetDefaultRadioChannelEvent>>(OnGetDefault);
|
||||||
SubscribeLocalEvent<HeadsetComponent, GotEquippedEvent>(OnGotEquipped);
|
SubscribeLocalEvent<HeadsetComponent, GotEquippedEvent>(OnGotEquipped);
|
||||||
SubscribeLocalEvent<HeadsetComponent, GotUnequippedEvent>(OnGotUnequipped);
|
SubscribeLocalEvent<HeadsetComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||||
|
SubscribeLocalEvent<HeadsetComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetDefault(EntityUid uid, HeadsetComponent component, InventoryRelayedEvent<GetDefaultRadioChannelEvent> args)
|
private void OnGetDefault(EntityUid uid, HeadsetComponent component, InventoryRelayedEvent<GetDefaultRadioChannelEvent> args)
|
||||||
@@ -29,10 +32,21 @@ public abstract class SharedHeadsetSystem : EntitySystem
|
|||||||
protected virtual void OnGotEquipped(EntityUid uid, HeadsetComponent component, GotEquippedEvent args)
|
protected virtual void OnGotEquipped(EntityUid uid, HeadsetComponent component, GotEquippedEvent args)
|
||||||
{
|
{
|
||||||
component.IsEquipped = args.SlotFlags.HasFlag(component.RequiredSlot);
|
component.IsEquipped = args.SlotFlags.HasFlag(component.RequiredSlot);
|
||||||
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
protected virtual void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
component.IsEquipped = false;
|
component.IsEquipped = false;
|
||||||
|
Dirty(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(Entity<HeadsetComponent> ent, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Enabled)
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.SurveillanceCamera;
|
namespace Content.Shared.SurveillanceCamera.Components;
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
[Access(typeof(SurveillanceCameraSystem))]
|
[Access(typeof(SharedSurveillanceCameraSystem))]
|
||||||
public sealed partial class SurveillanceCameraComponent : Component
|
public sealed partial class SurveillanceCameraComponent : Component
|
||||||
{
|
{
|
||||||
// List of active viewers. This is for bookkeeping purposes,
|
// List of active viewers. This is for bookkeeping purposes,
|
||||||
@@ -24,23 +25,20 @@ public sealed partial class SurveillanceCameraComponent : Component
|
|||||||
|
|
||||||
// If this camera is active or not. Deactivating a camera
|
// If this camera is active or not. Deactivating a camera
|
||||||
// will not allow it to obtain any new viewers.
|
// will not allow it to obtain any new viewers.
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public bool Active { get; set; } = true;
|
public bool Active = true;
|
||||||
|
|
||||||
// This one isn't easy to deal with. Will require a UI
|
// This one isn't easy to deal with. Will require a UI
|
||||||
// to change/set this so mapping these in isn't
|
// to change/set this so mapping these in isn't
|
||||||
// the most terrible thing possible.
|
// the most terrible thing possible.
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("id")]
|
[DataField("id")]
|
||||||
public string CameraId { get; set; } = "camera";
|
public string CameraId = "camera";
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[DataField("nameSet")]
|
public bool NameSet;
|
||||||
public bool NameSet { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
[DataField("networkSet")]
|
public bool NetworkSet;
|
||||||
public bool NetworkSet { get; set; }
|
|
||||||
|
|
||||||
// This has to be device network frequency prototypes.
|
// This has to be device network frequency prototypes.
|
||||||
[DataField("setupAvailableNetworks")]
|
[DataField("setupAvailableNetworks")]
|
||||||
@@ -1,8 +1,56 @@
|
|||||||
using Robust.Shared.GameStates;
|
using Content.Shared.Emp;
|
||||||
|
using Content.Shared.SurveillanceCamera.Components;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.SurveillanceCamera;
|
namespace Content.Shared.SurveillanceCamera;
|
||||||
|
|
||||||
|
public abstract class SharedSurveillanceCameraSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<SurveillanceCameraComponent, GetVerbsEvent<AlternativeVerb>>(AddVerbs);
|
||||||
|
SubscribeLocalEvent<SurveillanceCameraComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
|
SubscribeLocalEvent<SurveillanceCameraComponent, EmpDisabledRemovedEvent>(OnEmpDisabledRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddVerbs(EntityUid uid, SurveillanceCameraComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanInteract || !args.CanComplexInteract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.NameSet && component.NetworkSet)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AlternativeVerb verb = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("surveillance-camera-setup"),
|
||||||
|
Act = () => OpenSetupInterface(uid, args.User, component)
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(EntityUid uid, SurveillanceCameraComponent component, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
if (component.Active)
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
SetActive(uid, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmpDisabledRemoved(EntityUid uid, SurveillanceCameraComponent component, ref EmpDisabledRemovedEvent args)
|
||||||
|
{
|
||||||
|
SetActive(uid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: predict the rest of the server side system
|
||||||
|
public virtual void SetActive(EntityUid camera, bool setting, SurveillanceCameraComponent? component = null) { }
|
||||||
|
|
||||||
|
protected virtual void OpenSetupInterface(EntityUid uid, EntityUid player, SurveillanceCameraComponent? camera = null) { }
|
||||||
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum SurveillanceCameraVisualsKey : byte
|
public enum SurveillanceCameraVisualsKey : byte
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public sealed partial class TimerTriggerComponent : Component
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity that activated this trigger.
|
/// The entity that activated this trigger.
|
||||||
|
/// TODO: use WeakEntityReference once the engine PR is merged!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? User;
|
public EntityUid? User;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ namespace Content.Shared.Trigger.Systems;
|
|||||||
public sealed class EmpOnTriggerSystem : EntitySystem
|
public sealed class EmpOnTriggerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedEmpSystem _emp = default!;
|
[Dependency] private readonly SharedEmpSystem _emp = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -25,7 +24,7 @@ public sealed class EmpOnTriggerSystem : EntitySystem
|
|||||||
if (target == null)
|
if (target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_emp.EmpPulse(_transform.GetMapCoordinates(target.Value), ent.Comp.Range, ent.Comp.EnergyConsumption, (float)ent.Comp.DisableDuration.TotalSeconds);
|
_emp.EmpPulse(Transform(target.Value).Coordinates, ent.Comp.Range, ent.Comp.EnergyConsumption, ent.Comp.DisableDuration, args.User);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ public sealed partial class TriggerSystem
|
|||||||
|
|
||||||
if (timer.NextTrigger <= curTime)
|
if (timer.NextTrigger <= curTime)
|
||||||
{
|
{
|
||||||
Trigger(uid, timer.User, timer.KeyOut);
|
var user = TerminatingOrDeleted(timer.User) ? null : timer.User;
|
||||||
|
Trigger(uid, user, timer.KeyOut);
|
||||||
// Remove after triggering to prevent it from starting the timer again
|
// Remove after triggering to prevent it from starting the timer again
|
||||||
RemComp<ActiveTimerTriggerComponent>(uid);
|
RemComp<ActiveTimerTriggerComponent>(uid);
|
||||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public sealed partial class TriggerSystem : EntitySystem
|
|||||||
ent.Comp.NextTrigger = curTime + ent.Comp.Delay;
|
ent.Comp.NextTrigger = curTime + ent.Comp.Delay;
|
||||||
var delay = ent.Comp.InitialBeepDelay ?? ent.Comp.BeepInterval;
|
var delay = ent.Comp.InitialBeepDelay ?? ent.Comp.BeepInterval;
|
||||||
ent.Comp.NextBeep = curTime + delay;
|
ent.Comp.NextBeep = curTime + delay;
|
||||||
|
ent.Comp.User = user;
|
||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
|
|
||||||
var ev = new ActiveTimerTriggerEvent(user);
|
var ev = new ActiveTimerTriggerEvent(user);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
using Content.Shared.Emag.Components;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Advertise.Components;
|
using Content.Shared.Advertise.Components;
|
||||||
using Content.Shared.Advertise.Systems;
|
using Content.Shared.Advertise.Systems;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Emag.Components;
|
||||||
using Content.Shared.Emag.Systems;
|
using Content.Shared.Emag.Systems;
|
||||||
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Power.EntitySystems;
|
using Content.Shared.Power.EntitySystems;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<VendingMachineComponent, ComponentGetState>(OnVendingGetState);
|
SubscribeLocalEvent<VendingMachineComponent, ComponentGetState>(OnVendingGetState);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnRestockDoAfter);
|
SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnRestockDoAfter);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
@@ -83,6 +84,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
|
|||||||
EjectEnd = component.EjectEnd,
|
EjectEnd = component.EjectEnd,
|
||||||
DenyEnd = component.DenyEnd,
|
DenyEnd = component.DenyEnd,
|
||||||
DispenseOnHitEnd = component.DispenseOnHitEnd,
|
DispenseOnHitEnd = component.DispenseOnHitEnd,
|
||||||
|
Broken = component.Broken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +147,16 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
|
|||||||
RestockInventoryFromPrototype(uid, component, component.InitialStockQuality);
|
RestockInventoryFromPrototype(uid, component, component.InitialStockQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmpPulse(Entity<VendingMachineComponent> ent, ref EmpPulseEvent args)
|
||||||
|
{
|
||||||
|
if (!ent.Comp.Broken && _receiver.IsPowered(ent.Owner))
|
||||||
|
{
|
||||||
|
args.Affected = true;
|
||||||
|
args.Disabled = true;
|
||||||
|
ent.Comp.NextEmpEject = Timing.CurTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = null, bool forceEject = false) { }
|
protected virtual void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = null, bool forceEject = false) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ namespace Content.Shared.VendingMachines
|
|||||||
|
|
||||||
public string? NextItemToEject;
|
public string? NextItemToEject;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public bool Broken;
|
public bool Broken;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -300,5 +301,7 @@ namespace Content.Shared.VendingMachines
|
|||||||
public TimeSpan? DenyEnd;
|
public TimeSpan? DenyEnd;
|
||||||
|
|
||||||
public TimeSpan? DispenseOnHitEnd;
|
public TimeSpan? DispenseOnHitEnd;
|
||||||
|
|
||||||
|
public bool Broken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -167,6 +167,9 @@ ui-options-static-storage-ui = Lock storage window to hotbar
|
|||||||
|
|
||||||
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
||||||
ui-options-function-smart-equip-belt = Smart-equip to belt
|
ui-options-function-smart-equip-belt = Smart-equip to belt
|
||||||
|
ui-options-function-smart-equip-suit-storage = Smart-equip to suit storage
|
||||||
|
ui-options-function-smart-equip-pocket1 = Smart-equip to pocket 1
|
||||||
|
ui-options-function-smart-equip-pocket2 = Smart-equip to pocket 2
|
||||||
ui-options-function-open-backpack = Open backpack
|
ui-options-function-open-backpack = Open backpack
|
||||||
ui-options-function-open-belt = Open belt
|
ui-options-function-open-belt = Open belt
|
||||||
ui-options-function-throw-item-in-hand = Throw item
|
ui-options-function-throw-item-in-hand = Throw item
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -288,6 +288,8 @@
|
|||||||
tags:
|
tags:
|
||||||
- CorgiWearable
|
- CorgiWearable
|
||||||
- WhitelistChameleon
|
- WhitelistChameleon
|
||||||
|
- type: AddAccentClothing
|
||||||
|
accent: BarkAccent
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingOuterBase
|
parent: ClothingOuterBase
|
||||||
|
|||||||
@@ -8,17 +8,14 @@
|
|||||||
drawdepth: Effects
|
drawdepth: Effects
|
||||||
noRot: true
|
noRot: true
|
||||||
layers:
|
layers:
|
||||||
- shader: unshaded
|
- shader: unshaded
|
||||||
map: ["enum.EffectLayers.Unshaded"]
|
map: ["enum.EffectLayers.Unshaded"]
|
||||||
sprite: Effects/emp.rsi
|
sprite: Effects/emp.rsi
|
||||||
state: emp_pulse
|
state: emp_pulse
|
||||||
- type: EffectVisuals
|
- type: EffectVisuals
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- HideContextMenu
|
- HideContextMenu
|
||||||
- type: EmitSoundOnSpawn
|
|
||||||
sound:
|
|
||||||
path: /Audio/Effects/Lightning/lightningbolt.ogg
|
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -31,12 +28,12 @@
|
|||||||
drawdepth: Effects
|
drawdepth: Effects
|
||||||
noRot: true
|
noRot: true
|
||||||
layers:
|
layers:
|
||||||
- shader: unshaded
|
- shader: unshaded
|
||||||
map: ["enum.EffectLayers.Unshaded"]
|
map: ["enum.EffectLayers.Unshaded"]
|
||||||
sprite: Effects/emp.rsi
|
sprite: Effects/emp.rsi
|
||||||
state: emp_disable
|
state: emp_disable
|
||||||
- type: EffectVisuals
|
- type: EffectVisuals
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- HideContextMenu
|
- HideContextMenu
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 3
|
Piercing: 14
|
||||||
Heat: 16
|
Heat: 5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BulletLightRifleUranium
|
id: BulletLightRifleUranium
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 3
|
Piercing: 26
|
||||||
Heat: 32
|
Heat: 9
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BulletMagnumAP
|
id: BulletMagnumAP
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 2
|
Piercing: 12
|
||||||
Heat: 14
|
Heat: 4
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BulletPistolUranium
|
id: BulletPistolUranium
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 2
|
Piercing: 12
|
||||||
Heat: 15
|
Heat: 5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BulletRifleUranium
|
id: BulletRifleUranium
|
||||||
|
|||||||
@@ -68,8 +68,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 3
|
Piercing: 7
|
||||||
Heat: 7
|
Heat: 3
|
||||||
- type: IgnitionSource
|
- type: IgnitionSource
|
||||||
ignited: true
|
ignited: true
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,8 @@
|
|||||||
- type: Projectile
|
- type: Projectile
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Blunt: 14
|
Piercing: 10
|
||||||
|
Heat: 4
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: true
|
enabled: true
|
||||||
color: "#ff4300"
|
color: "#ff4300"
|
||||||
|
|||||||
@@ -104,7 +104,15 @@
|
|||||||
description: A direction sign, pointing out which way evac is.
|
description: A direction sign, pointing out which way evac is.
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: direction_evac
|
layers:
|
||||||
|
- state: direction_evac
|
||||||
|
- state: direction_evac_glow
|
||||||
|
shader: unshaded
|
||||||
|
- state: direction_evac_glow
|
||||||
|
shader: shaded
|
||||||
|
#This is a neat trick I found to sort of "hack" an emissive map into ss14. Basically, the direction_evac_glow texture has an alpha channel.
|
||||||
|
#Alpha doesn't work for unshaded, but for *shaded* it does, and by putting a shaded texture infront of the unshaded, we can dim the unshaded texture, effectively allowing brightness control.
|
||||||
|
#I am re-using this from my high-vis vest PR, where I go further into detail, https://github.com/space-wizards/space-station-14/pull/37869
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseSignDirectional
|
parent: BaseSignDirectional
|
||||||
@@ -288,6 +296,15 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: armory
|
state: armory
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseSign
|
||||||
|
id: SignArrivals
|
||||||
|
name: arrivals sign
|
||||||
|
description: A sign indicating where the Arrivals shuttle will dock.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: arrivals
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseSign
|
parent: BaseSign
|
||||||
id: SignToolStorage
|
id: SignToolStorage
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
- node: start
|
- node: start
|
||||||
edges:
|
edges:
|
||||||
- to: msstunprod
|
- to: msstunprod
|
||||||
|
completed:
|
||||||
|
- !type:AdminLog
|
||||||
|
message: "Construction"
|
||||||
|
impact: High
|
||||||
steps:
|
steps:
|
||||||
- material: MetalRod
|
- material: MetalRod
|
||||||
amount: 1
|
amount: 1
|
||||||
|
|||||||
BIN
Resources/Textures/Structures/Wallmounts/signs.rsi/arrivals.png
Normal file
BIN
Resources/Textures/Structures/Wallmounts/signs.rsi/arrivals.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 261 B |
@@ -5,7 +5,7 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris at commit 4e0bbe682d0a00192d24708fdb7031008aa03f18 and bee station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/13dd5ac712385642574138f6d7b30eea7c2fab9c, Job signs by EmoGarbage404 (github) with inspiration from yogstation and tgstation, 'direction_exam' and 'direction_icu' made by rosieposieeee (github), 'direction_atmos' made by SlamBamActionman, 'vox' based on sprites taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/e7f005f8b8d3f7d89cbee3b87f76c23f9e951c27/icons/obj/decals.dmi, 'direction_pods' derived by WarPigeon from existing directional signs., 'detective' derived by Soupkilove from existing security sign",
|
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris at commit 4e0bbe682d0a00192d24708fdb7031008aa03f18 and bee station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/13dd5ac712385642574138f6d7b30eea7c2fab9c, Job signs by EmoGarbage404 (github) with inspiration from yogstation and tgstation, 'direction_exam' and 'direction_icu' made by rosieposieeee (github), 'direction_atmos' made by SlamBamActionman, 'vox' based on sprites taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/e7f005f8b8d3f7d89cbee3b87f76c23f9e951c27/icons/obj/decals.dmi, 'direction_pods' derived by WarPigeon from existing directional signs., 'detective' derived by Soupkilove from existing security sign, direction_evac_glow made by moomoobeef based on existing direction_evac, 'arrivals' made by SlamBamActionman",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "ai"
|
"name": "ai"
|
||||||
@@ -28,6 +28,9 @@
|
|||||||
{
|
{
|
||||||
"name": "armory"
|
"name": "armory"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "arrivals"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "barbershop"
|
"name": "barbershop"
|
||||||
},
|
},
|
||||||
@@ -165,6 +168,10 @@
|
|||||||
"name": "direction_evac",
|
"name": "direction_evac",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "direction_evac_glow",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "direction_supply",
|
"name": "direction_supply",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
|||||||
@@ -255,6 +255,18 @@ binds:
|
|||||||
type: State
|
type: State
|
||||||
key: E
|
key: E
|
||||||
mod1: Shift
|
mod1: Shift
|
||||||
|
- function: SmartEquipPocket1
|
||||||
|
type: State
|
||||||
|
key: F
|
||||||
|
mod1: Shift
|
||||||
|
- function: SmartEquipPocket2
|
||||||
|
type: State
|
||||||
|
key: G
|
||||||
|
mod1: Shift
|
||||||
|
- function: SmartEquipSuitStorage
|
||||||
|
type: State
|
||||||
|
key: H
|
||||||
|
mod1: Shift
|
||||||
- function: OpenBackpack
|
- function: OpenBackpack
|
||||||
type: State
|
type: State
|
||||||
key: V
|
key: V
|
||||||
|
|||||||
Reference in New Issue
Block a user