mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-02-14 19:29:57 +01:00
merge mini-upstream
This commit is contained in:
41
.github/ISSUE_TEMPLATE/01_report_issue.yml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/01_report_issue.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Report an issue
|
||||
description: For issues that are not related to any other issue form.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue. This template is intended for any issues that do not fit other issue templates.
|
||||
For feedback or help running Space Station 14, please join our [Discord](https://discord.gg/rGvu9hKffJ).
|
||||
- type: input
|
||||
attributes:
|
||||
label: What version did the issue occur in?
|
||||
description: You can find the version by opening the changelog in-game and looking at the bottom right corner of the changelog window.
|
||||
placeholder: wizards/aa1337b
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
A clear and concise description of what the issue is.
|
||||
If the issue is visual in nature, consider posting a screenshot.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: List the steps required to reproduce the issue.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other additional context about the issue here.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
48
.github/ISSUE_TEMPLATE/02_mapping_issue.yml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/02_mapping_issue.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Report an mapping issue
|
||||
description: For issues regarding mapping.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue. This template is intended for any issues related to mapping.
|
||||
For feedback or help running Space Station 14, please join our [Discord](https://discord.gg/rGvu9hKffJ).
|
||||
- type: input
|
||||
attributes:
|
||||
label: What version did the issue occur in?
|
||||
description: You can find the version by opening the changelog in-game and looking at the bottom right corner of the changelog window.
|
||||
placeholder: wizards/aa1337b
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: On what station, shuttle, or grid did the issue occur on?
|
||||
description: The name of the station, shuttle, or grid. If you do not know the name, try to describe it.
|
||||
placeholder: Bagel
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
A clear and concise description of what the issue is.
|
||||
If the issue is visual in nature, consider posting a screenshot.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: List the steps required to reproduce the issue.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other additional context about the issue here.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,3 +1,4 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Предложение
|
||||
url: https://discord.station14.ru
|
||||
|
||||
@@ -7,6 +7,18 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
{
|
||||
[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)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -16,7 +28,7 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,18 +79,21 @@ namespace Content.Client.Entry
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly ClientsidePlaytimeTrackingManager _clientsidePlaytimeManager = default!;
|
||||
|
||||
public override void Init()
|
||||
public override void PreInit()
|
||||
{
|
||||
ClientContentIoC.Register();
|
||||
ClientContentIoC.Register(Dependencies);
|
||||
|
||||
foreach (var callback in TestingCallbacks)
|
||||
{
|
||||
var cast = (ClientModuleTestingCallbacks) callback;
|
||||
cast.ClientBeforeIoC?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
IoCManager.BuildGraph();
|
||||
IoCManager.InjectDependencies(this);
|
||||
public override void Init()
|
||||
{
|
||||
Dependencies.BuildGraph();
|
||||
Dependencies.InjectDependencies(this);
|
||||
|
||||
_contentLoc.Initialize();
|
||||
_componentFactory.DoAutoRegistrations();
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
|
||||
_dialogIsOpen = true;
|
||||
var filters = new FileDialogFilters(new FileDialogFilters.Group("txt"));
|
||||
await using var file = await _fileDialogManager.OpenFile(filters);
|
||||
await using var file = await _fileDialogManager.OpenFile(filters, FileAccess.Read);
|
||||
_dialogIsOpen = false;
|
||||
|
||||
if (_window == null || _window.Disposed || file == null)
|
||||
|
||||
@@ -73,6 +73,9 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipPocket1);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipPocket2);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipSuitStorage);
|
||||
human.AddFunction(ContentKeyFunctions.OpenBackpack);
|
||||
human.AddFunction(ContentKeyFunctions.OpenBelt);
|
||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace Content.Client.Instruments.UI
|
||||
// or focus the previously-opened window.
|
||||
_isMidiFileDialogueWindowOpen = true;
|
||||
|
||||
await using var file = await _dialogs.OpenFile(filters);
|
||||
await using var file = await _dialogs.OpenFile(filters, FileAccess.Read);
|
||||
|
||||
_isMidiFileDialogueWindowOpen = false;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ using Content.Client.Lobby;
|
||||
using Content.Client.Players.RateLimiting;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.IoC;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Players.RateLimiting;
|
||||
|
||||
@@ -31,10 +32,9 @@ namespace Content.Client.IoC
|
||||
{
|
||||
internal static class ClientContentIoC
|
||||
{
|
||||
public static void Register()
|
||||
public static void Register(IDependencyCollection collection)
|
||||
{
|
||||
var collection = IoCManager.Instance!;
|
||||
|
||||
SharedContentIoC.Register(collection);
|
||||
collection.Register<IParallaxManager, ParallaxManager>();
|
||||
collection.Register<GeneratedParallaxCache>();
|
||||
collection.Register<IChatManager, ChatManager>();
|
||||
|
||||
@@ -1602,7 +1602,7 @@ namespace Content.Client.Lobby.UI
|
||||
return;
|
||||
|
||||
StartExport();
|
||||
await using var file = await _dialogManager.OpenFile(new FileDialogFilters(new FileDialogFilters.Group("yml")));
|
||||
await using var file = await _dialogManager.OpenFile(new FileDialogFilters(new FileDialogFilters.Group("yml")), FileAccess.Read);
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
|
||||
@@ -190,6 +190,9 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddHeader("ui-options-header-interaction-adv");
|
||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||
AddButton(ContentKeyFunctions.SmartEquipBelt);
|
||||
AddButton(ContentKeyFunctions.SmartEquipPocket1);
|
||||
AddButton(ContentKeyFunctions.SmartEquipPocket2);
|
||||
AddButton(ContentKeyFunctions.SmartEquipSuitStorage);
|
||||
AddButton(ContentKeyFunctions.OpenBackpack);
|
||||
AddButton(ContentKeyFunctions.OpenBelt);
|
||||
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.DenyEnd = state.DenyEnd;
|
||||
component.DispenseOnHitEnd = state.DispenseOnHitEnd;
|
||||
component.Broken = state.Broken;
|
||||
|
||||
// If all we did was update amounts then we can leave BUI buttons in place.
|
||||
var fullUiUpdate = !component.Inventory.Keys.SequenceEqual(state.Inventory.Keys) ||
|
||||
|
||||
@@ -27,6 +27,16 @@ public sealed partial class TestPair : RobustIntegrationTest.TestPair
|
||||
|
||||
protected override async Task Initialize()
|
||||
{
|
||||
await base.Initialize();
|
||||
|
||||
// Prevent info log spam in some tests (particularly SpawnAndDeleteAllEntitiesOnDifferentMaps)
|
||||
Server.System<SharedMapSystem>().Log.Level = LogLevel.Warning;
|
||||
Client.EntMan.EntitySysManager.SystemLoaded += (_, e) =>
|
||||
{
|
||||
if (e.System is SharedMapSystem map)
|
||||
map.Log.Level = LogLevel.Warning;
|
||||
};
|
||||
|
||||
var settings = (PoolSettings)Settings;
|
||||
if (!settings.DummyTicker)
|
||||
{
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public sealed class EmbedTest : InteractionTest
|
||||
"Target has unexpected EmbeddedObjects count.");
|
||||
|
||||
// Wait for the cooldown between throws
|
||||
await RunSeconds(Hands.ThrowCooldown.Seconds);
|
||||
await RunSeconds(Hands!.ThrowCooldown.Seconds);
|
||||
|
||||
// Throw the second projectile
|
||||
await ThrowItem();
|
||||
|
||||
@@ -169,6 +169,12 @@ public abstract partial class InteractionTest
|
||||
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
|
||||
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableToggleable = true)
|
||||
{
|
||||
if (Hands == null)
|
||||
{
|
||||
Assert.Fail("No HandsComponent");
|
||||
return default;
|
||||
}
|
||||
|
||||
if (Hands.ActiveHandId == null)
|
||||
{
|
||||
Assert.Fail("No active hand");
|
||||
@@ -210,6 +216,12 @@ public abstract partial class InteractionTest
|
||||
{
|
||||
entity ??= Target;
|
||||
|
||||
if (Hands == null)
|
||||
{
|
||||
Assert.Fail("No HandsComponent");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Hands.ActiveHandId == null)
|
||||
{
|
||||
Assert.Fail("No active hand");
|
||||
@@ -714,7 +726,7 @@ public abstract partial class InteractionTest
|
||||
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)
|
||||
@@ -730,6 +742,20 @@ public abstract partial class InteractionTest
|
||||
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
|
||||
|
||||
#region Entity lookups
|
||||
@@ -860,7 +886,7 @@ public abstract partial class InteractionTest
|
||||
/// List of currently active DoAfters on the player.
|
||||
/// </summary>
|
||||
protected IEnumerable<Shared.DoAfter.DoAfter> ActiveDoAfters
|
||||
=> DoAfters.DoAfters.Values.Where(x => !x.Cancelled && !x.Completed);
|
||||
=> DoAfters?.DoAfters.Values.Where(x => !x.Cancelled && !x.Completed) ?? [];
|
||||
|
||||
#region Component
|
||||
|
||||
@@ -974,9 +1000,9 @@ public abstract partial class InteractionTest
|
||||
/// <summary>
|
||||
/// Sends a bui message using the given bui key.
|
||||
/// </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;
|
||||
|
||||
await Client.WaitPost(() => bui.SendMessage(msg));
|
||||
@@ -988,9 +1014,9 @@ public abstract partial class InteractionTest
|
||||
/// <summary>
|
||||
/// Sends a bui message using the given bui key.
|
||||
/// </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;
|
||||
|
||||
await Client.WaitPost(() => bui.Close());
|
||||
@@ -1412,15 +1438,25 @@ public abstract partial class InteractionTest
|
||||
protected EntityUid? ToServer(NetEntity? nent) => SEntMan.GetEntity(nent);
|
||||
protected EntityUid? ToClient(NetEntity? nent) => CEntMan.GetEntity(nent);
|
||||
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? 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 ToClient(NetCoordinates coords) => CEntMan.GetCoordinates(coords);
|
||||
protected EntityCoordinates? ToServer(NetCoordinates? coords) => SEntMan.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
|
||||
|
||||
#region Metadata & Transforms
|
||||
|
||||
@@ -125,8 +125,8 @@ public abstract partial class InteractionTest
|
||||
protected SharedUserInterfaceSystem CUiSys = default!;
|
||||
|
||||
// player components
|
||||
protected HandsComponent Hands = default!;
|
||||
protected DoAfterComponent DoAfters = default!;
|
||||
protected HandsComponent? Hands;
|
||||
protected DoAfterComponent? DoAfters;
|
||||
|
||||
public float TickPeriod => (float)STiming.TickPeriod.TotalSeconds;
|
||||
|
||||
@@ -222,8 +222,8 @@ public abstract partial class InteractionTest
|
||||
SPlayer = SEntMan.SpawnEntity(PlayerPrototype, SEntMan.GetCoordinates(PlayerCoords));
|
||||
Player = SEntMan.GetNetEntity(SPlayer);
|
||||
Server.PlayerMan.SetAttachedEntity(ServerSession, SPlayer);
|
||||
Hands = SEntMan.GetComponent<HandsComponent>(SPlayer);
|
||||
DoAfters = SEntMan.GetComponent<DoAfterComponent>(SPlayer);
|
||||
Hands = SEntMan.GetComponentOrNull<HandsComponent>(SPlayer);
|
||||
DoAfters = SEntMan.GetComponentOrNull<DoAfterComponent>(SPlayer);
|
||||
});
|
||||
|
||||
// Check player got attached.
|
||||
|
||||
154
Content.IntegrationTests/Tests/Mousetrap/MousetrapTest.cs
Normal file
154
Content.IntegrationTests/Tests/Mousetrap/MousetrapTest.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Content.IntegrationTests.Tests.Movement;
|
||||
using Content.Server.NPC.HTN;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mousetrap;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Mousetrap;
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a mouse and a mousetrap.
|
||||
/// Makes the mouse cross the inactive mousetrap, then activates the trap and
|
||||
/// makes the mouse try to cross back over it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Yep, every time the tests run, a virtual mouse dies. Sorry.
|
||||
/// </remarks>
|
||||
public sealed class MousetrapMouseMoveOverTest : MovementTest
|
||||
{
|
||||
private static readonly EntProtoId MousetrapProtoId = "Mousetrap";
|
||||
private static readonly EntProtoId MouseProtoId = "MobMouse";
|
||||
protected override string PlayerPrototype => MouseProtoId.Id; // use a mouse as the player entity
|
||||
|
||||
[Test]
|
||||
public async Task MouseMoveOverTest()
|
||||
{
|
||||
// Make sure the mouse doesn't have any AI active
|
||||
await Server.WaitPost(() => SEntMan.RemoveComponent<HTNComponent>(SPlayer));
|
||||
|
||||
// Spawn a mouse trap
|
||||
await SpawnTarget(MousetrapProtoId);
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Mouse and mousetrap not in expected positions.");
|
||||
|
||||
Assert.That(HasComp<MousetrapComponent>(),
|
||||
$"{MousetrapProtoId} does not have a MousetrapComponent. If you're refactoring, please update this test!");
|
||||
|
||||
Assert.That(TryComp<ItemToggleComponent>(out var itemToggleComp),
|
||||
$"{MousetrapProtoId} does not have a ItemToggleComponent. If you're refactoring, please update this test!");
|
||||
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap started active.");
|
||||
|
||||
// The mouse is spawned by the test before the atmosphere is added, so it has some barotrauma damage already
|
||||
// TODO: fix this since it can have an impact on integration tests
|
||||
Assert.That(SEntMan.TryGetComponent<DamageableComponent>(SPlayer, out var damageComp),
|
||||
$"Player does not have a DamageableComponent.");
|
||||
var startingDamage = damageComp.TotalDamage;
|
||||
|
||||
Assert.That(SEntMan.TryGetComponent<MobStateComponent>(SPlayer, out var mouseMobStateComp),
|
||||
$"{MouseProtoId} does not have a MobStateComponent.");
|
||||
Assert.That(mouseMobStateComp.CurrentState, Is.EqualTo(MobState.Alive), "Mouse was not alive when spawned.");
|
||||
|
||||
// Move mouse over the trap
|
||||
await Move(DirectionFlag.East, 1f);
|
||||
|
||||
Assert.That(Delta(), Is.LessThan(0.5), "Mouse did not move over mousetrap.");
|
||||
|
||||
// Walking over an inactive trap does not trigger it
|
||||
Assert.That(damageComp.TotalDamage, Is.LessThanOrEqualTo(startingDamage), "Mouse took damage from inactive trap!");
|
||||
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was activated.");
|
||||
|
||||
// Activate the trap
|
||||
var itemToggleSystem = Server.System<ItemToggleSystem>();
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(itemToggleSystem.TrySetActive(STarget.Value, true), "Could not activate the mouse trap.");
|
||||
});
|
||||
|
||||
await Move(DirectionFlag.West, 1f);
|
||||
Assert.That(Delta(), Is.LessThan(0.1), "Mouse moved past active mousetrap.");
|
||||
|
||||
// Walking over an active trap triggers it
|
||||
Assert.That(damageComp.TotalDamage, Is.GreaterThan(startingDamage), "Mouse did not take damage from active trap!");
|
||||
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was not deactivated after triggering.");
|
||||
Assert.That(mouseMobStateComp.CurrentState, Is.EqualTo(MobState.Dead), "Mouse was not killed by trap.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a mousetrap and makes the player walk over it without shoes.
|
||||
/// Gives the player some shoes and makes them walk back over the trap.
|
||||
/// </summary>
|
||||
public sealed class MousetrapHumanMoveOverTest : MovementTest
|
||||
{
|
||||
private static readonly EntProtoId MousetrapProtoId = "Mousetrap";
|
||||
private const string ShoesProtoId = "InteractionTestShoes";
|
||||
|
||||
[TestPrototypes]
|
||||
private static readonly string TestPrototypes = $@"
|
||||
- type: entity
|
||||
parent: ClothingShoesBase
|
||||
id: {ShoesProtoId}
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Shoes/Boots/workboots.rsi
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task HumanMoveOverTest()
|
||||
{
|
||||
await SpawnTarget(MousetrapProtoId);
|
||||
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Player and mousetrap not in expected positions.");
|
||||
|
||||
Assert.That(HasComp<MousetrapComponent>(),
|
||||
$"{MousetrapProtoId} does not have a MousetrapComponent. If you're refactoring, please update this test!");
|
||||
|
||||
Assert.That(TryComp<ItemToggleComponent>(out var itemToggleComp),
|
||||
$"{MousetrapProtoId} does not have a ItemToggleComponent. If you're refactoring, please update this test!");
|
||||
|
||||
// Activate the trap
|
||||
var itemToggleSystem = Server.System<ItemToggleSystem>();
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(itemToggleSystem.TrySetActive(STarget.Value, true), "Could not activate the mouse trap.");
|
||||
});
|
||||
|
||||
Assert.That(SEntMan.TryGetComponent<DamageableComponent>(SPlayer, out var damageComp),
|
||||
$"Player does not have a DamageableComponent.");
|
||||
var startingDamage = damageComp.TotalDamage;
|
||||
|
||||
// Move player over the trap
|
||||
await Move(DirectionFlag.East, 0.5f);
|
||||
|
||||
Assert.That(Delta(), Is.LessThan(0.5), "Player did not move over mousetrap.");
|
||||
|
||||
// Walking over the trap without shoes activates it
|
||||
Assert.That(damageComp.TotalDamage, Is.GreaterThan(startingDamage), "Player did not take damage.");
|
||||
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was not deactivated after triggering.");
|
||||
|
||||
// Reactivate the trap
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(itemToggleSystem.TrySetActive(STarget.Value, true), "Could not activate the mouse trap.");
|
||||
});
|
||||
var afterStepDamage = damageComp.TotalDamage;
|
||||
|
||||
// Give the player some shoes
|
||||
await PlaceInHands(ShoesProtoId);
|
||||
// Thanks to quick-equip, using the shoes will wear them
|
||||
await UseInHand();
|
||||
|
||||
// Move back over the trap
|
||||
await Move(DirectionFlag.West, 1f);
|
||||
Assert.That(Delta(), Is.GreaterThan(0.5), "Player did not move back over mousetrap.");
|
||||
|
||||
// Walking over the trap with shoes on does not activate it
|
||||
Assert.That(damageComp.TotalDamage, Is.LessThanOrEqualTo(afterStepDamage), "Player took damage from trap!");
|
||||
Assert.That(itemToggleComp.Activated, "Mousetrap was deactivated despite the player being protected by shoes.");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
#nullable enable
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Power.Nodes;
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.NodeContainer;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Maps;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.NodeContainer;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ public static class ServerPackaging
|
||||
new PlatformReg("osx-x64", "MacOS", true),
|
||||
new PlatformReg("osx-arm64", "MacOS", true),
|
||||
// Non-default platforms (i.e. for Watchdog Git)
|
||||
new PlatformReg("win-x86", "Windows", false),
|
||||
new PlatformReg("linux-x86", "Linux", false),
|
||||
new PlatformReg("linux-arm", "Linux", false),
|
||||
new PlatformReg("freebsd-x64", "FreeBSD", false),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Threading;
|
||||
using Content.Server.Administration.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
@@ -6,8 +5,8 @@ using Content.Server.Body.Systems;
|
||||
using Content.Server.Electrocution;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.GhostKick;
|
||||
using Content.Server.Medical;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Server.Physics.Components;
|
||||
using Content.Server.Pointing.Components;
|
||||
using Content.Server.Polymorph.Systems;
|
||||
using Content.Server.Popups;
|
||||
@@ -22,8 +21,8 @@ using Content.Shared.Administration.Components;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Clumsy;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Clumsy;
|
||||
using Content.Shared.Cluwne;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Systems;
|
||||
@@ -32,6 +31,7 @@ using Content.Shared.Electrocution;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Medical;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
@@ -54,7 +54,10 @@ using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Spawners;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
@@ -63,7 +66,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
private readonly ProtoId<PolymorphPrototype> LizardSmite = "AdminLizardSmite";
|
||||
private readonly ProtoId<PolymorphPrototype> VulpkaninSmite = "AdminVulpSmite";
|
||||
|
||||
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||
@@ -143,7 +146,6 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
_sharedGodmodeSystem.EnableGodmode(args.Target); // So they don't suffocate.
|
||||
EnsureComp<TabletopDraggableComponent>(args.Target);
|
||||
RemComp<PhysicsComponent>(args.Target); // So they can be dragged around.
|
||||
var xform = Transform(args.Target);
|
||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-chess-self"), args.Target,
|
||||
args.Target, PopupType.LargeCaution);
|
||||
@@ -1006,5 +1008,63 @@ public sealed partial class AdminVerbSystem
|
||||
Message = string.Join(": ", siliconName, Loc.GetString("admin-smite-silicon-laws-bound-description"))
|
||||
};
|
||||
args.Verbs.Add(silicon);
|
||||
|
||||
var homingRodName = Loc.GetString("admin-smite-homing-rod-name").ToLowerInvariant();
|
||||
Verb homingRod = new()
|
||||
{
|
||||
Text = homingRodName,
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("Objects/Specific/Security/target.rsi"), "target_s"),
|
||||
Act = () =>
|
||||
{
|
||||
var speed = 25f; // It don't miss brother.
|
||||
var distance = 350f;
|
||||
HomingLaunchSequence(args.Target, "ImmovableRodKeepTiles", distance, speed); // todo: swap the proto for an EntityTable GetSpawns once rod rule rework
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = string.Join(": ", homingRodName, Loc.GetString("admin-smite-homing-rod-description"))
|
||||
};
|
||||
args.Verbs.Add(homingRod);
|
||||
|
||||
var homingRodSlowName = Loc.GetString("admin-smite-homing-rod-slow-name").ToLowerInvariant();
|
||||
Verb homingRodSlow = new()
|
||||
{
|
||||
Text = homingRodSlowName,
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("Objects/Specific/Security/target.rsi"), "target_c"),
|
||||
Act = () =>
|
||||
{
|
||||
var speed = 5f; // slightly faster than default sprint speed 4.5
|
||||
if (TryComp<MovementSpeedModifierComponent>(args.Target, out var movement))
|
||||
speed = movement.CurrentSprintSpeed + 0.001f;// run
|
||||
var distance = 200f; // its kinda slow so were just gonna cheat a bit.
|
||||
HomingLaunchSequence(args.Target, "ImmovableRodKeepTiles", distance, speed);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = string.Join(": ", homingRodSlowName, Loc.GetString("admin-smite-homing-rod-slow-description"))
|
||||
};
|
||||
args.Verbs.Add(homingRodSlow);
|
||||
}
|
||||
|
||||
public void HomingLaunchSequence(EntityUid target, EntProtoId proto, float distance, float speed)
|
||||
{
|
||||
// ToDo: Reuse some spawning code from whereever the rod rule ends up.
|
||||
// I would do it now but theres a massive rod rewrite, and I don't wanna poke it for this.
|
||||
// find reasonable spawn location (use gamerule and find rod?) but respect map not on grid etc etc
|
||||
|
||||
var offset = new Random(target.Id).NextAngle().RotateVec(new Vector2(distance, 0));
|
||||
var spawnCoords = _transformSystem.GetMapCoordinates(target).Offset(offset);
|
||||
var rod = Spawn(proto, spawnCoords);
|
||||
// Here we abuse the ChasingWalkComp by making it skip targetting logic and dialling its frequency up
|
||||
EnsureComp<ChasingWalkComponent>(rod, out var chasingComp);
|
||||
chasingComp.NextChangeVectorTime = TimeSpan.MaxValue; // we just want it to never change
|
||||
chasingComp.ChasingEntity = target;
|
||||
chasingComp.ImpulseInterval = .1f; // skrrt skrrrrrrt skrrrt
|
||||
chasingComp.RotateWithImpulse = true;
|
||||
chasingComp.MaxSpeed = speed;
|
||||
chasingComp.Speed = speed; // tell me lies, tell me sweet little lies.
|
||||
|
||||
if (TryComp<TimedDespawnComponent>(rod, out var despawn))
|
||||
despawn.Lifetime = offset.Length() / speed * 3; // exists thrice as long as it takes to get to you.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Station.Components;
|
||||
using Content.Shared.Verbs;
|
||||
|
||||
@@ -126,6 +126,9 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
|
||||
if (_random.Prob(anomaly.Comp.Continuity))
|
||||
SetBehavior(anomaly, GetRandomBehavior());
|
||||
}
|
||||
|
||||
var ev = new AnomalyAffectedByParticleEvent(anomaly, args.OtherEntity);
|
||||
RaiseLocalEvent(anomaly, ref ev);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Server.Anomaly.Components;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
|
||||
public sealed class ShuffleParticlesAnomalySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnomalySystem _anomaly = default!;
|
||||
@@ -12,19 +12,16 @@ public sealed class ShuffleParticlesAnomalySystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<ShuffleParticlesAnomalyComponent, AnomalyPulseEvent>(OnPulse);
|
||||
SubscribeLocalEvent<ShuffleParticlesAnomalyComponent, StartCollideEvent>(OnStartCollide);
|
||||
SubscribeLocalEvent<ShuffleParticlesAnomalyComponent, AnomalyAffectedByParticleEvent>(OnAffectedByParticle);
|
||||
}
|
||||
|
||||
private void OnStartCollide(Entity<ShuffleParticlesAnomalyComponent> ent, ref StartCollideEvent args)
|
||||
private void OnAffectedByParticle(Entity<ShuffleParticlesAnomalyComponent> ent, ref AnomalyAffectedByParticleEvent args)
|
||||
{
|
||||
if (!TryComp<AnomalyComponent>(ent, out var anomaly))
|
||||
return;
|
||||
|
||||
if (!HasComp<AnomalousParticleComponent>(args.OtherEntity))
|
||||
if (!TryComp<AnomalyComponent>(ent, out var anomalyComp))
|
||||
return;
|
||||
|
||||
if (ent.Comp.ShuffleOnParticleHit && _random.Prob(ent.Comp.Prob))
|
||||
_anomaly.ShuffleParticlesEffect((ent, anomaly));
|
||||
_anomaly.ShuffleParticlesEffect((args.Anomaly, anomalyComp));
|
||||
}
|
||||
|
||||
private void OnPulse(Entity<ShuffleParticlesAnomalyComponent> ent, ref AnomalyPulseEvent args)
|
||||
|
||||
@@ -174,7 +174,10 @@ public sealed partial class AntagSelectionSystem
|
||||
if (roles.Count == 0)
|
||||
return false;
|
||||
|
||||
var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter;
|
||||
if (!_pref.TryGetCachedPreferences(session.UserId, out var pref))
|
||||
return false;
|
||||
|
||||
var character = (HumanoidCharacterProfile) pref.SelectedCharacter;
|
||||
|
||||
var valid = false;
|
||||
|
||||
@@ -183,8 +186,7 @@ public sealed partial class AntagSelectionSystem
|
||||
{
|
||||
var list = new List<ProtoId<AntagPrototype>>{role};
|
||||
|
||||
|
||||
if (pref.AntagPreferences.Contains(role)
|
||||
if (character.AntagPreferences.Contains(role)
|
||||
&& !_ban.IsRoleBanned(session, list)
|
||||
&& _playTime.IsAllowed(session, list))
|
||||
valid = true;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Arcade;
|
||||
using Content.Shared.Dataset;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -23,85 +24,73 @@ public sealed partial class SpaceVillainArcadeComponent : SharedSpaceVillainArca
|
||||
/// <summary>
|
||||
/// The sound played when a new session of the SpaceVillain game is begun.
|
||||
/// </summary>
|
||||
[DataField("newGameSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier NewGameSound = new SoundPathSpecifier("/Audio/Effects/Arcade/newgame.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the player chooses to attack.
|
||||
/// </summary>
|
||||
[DataField("playerAttackSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier PlayerAttackSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_attack.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the player chooses to heal.
|
||||
/// </summary>
|
||||
[DataField("playerHealSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier PlayerHealSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_heal.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the player chooses to regain mana.
|
||||
/// </summary>
|
||||
[DataField("playerChargeSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier PlayerChargeSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_charge.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the player wins.
|
||||
/// </summary>
|
||||
[DataField("winSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier WinSound = new SoundPathSpecifier("/Audio/Effects/Arcade/win.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The sound played when the player loses.
|
||||
/// </summary>
|
||||
[DataField("gameOverSound")]
|
||||
[DataField]
|
||||
public SoundSpecifier GameOverSound = new SoundPathSpecifier("/Audio/Effects/Arcade/gameover.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The prefixes that can be used to create the game name.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("possibleFightVerbs")]
|
||||
public List<string> PossibleFightVerbs = new()
|
||||
{"Победи", "Аннигилируй", "Спаси", "Ударь", "Останови", "Уничтожь", "Заробасти", "Добейся", "Отымей", "Заовни"};
|
||||
[DataField]
|
||||
public ProtoId<LocalizedDatasetPrototype> PossibleFightVerbs = "SpaceVillainVerbsFight";
|
||||
|
||||
/// <summary>
|
||||
/// The first names/titles that can be used to construct the name of the villain.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("possibleFirstEnemyNames")]
|
||||
public List<string> PossibleFirstEnemyNames = new(){
|
||||
"Автоматический", "Фермер", "Лорд", "Профессор", "Кубинец", "Злой", "Грозный Король",
|
||||
"Космический", "Лорд", "Могучий", "Герцог", "Генерал"
|
||||
};
|
||||
[DataField]
|
||||
public ProtoId<LocalizedDatasetPrototype> PossibleFirstEnemyNames = "SpaceVillainNamesEnemyFirst";
|
||||
|
||||
/// <summary>
|
||||
/// The last names that can be used to construct the name of the villain.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("possibleLastEnemyNames")]
|
||||
public List<string> PossibleLastEnemyNames = new()
|
||||
{
|
||||
"Мелоноид", "Киллертрон", "Волшебник", "Руина", "Джефф", "Эктоплазма", "Крушелон", "Ухангоид",
|
||||
"Вакоид", "Петеоид", "слайм", "Грифер", "ЕРПшер", "Человек-ящерица", "Единорог"
|
||||
};
|
||||
[DataField]
|
||||
public ProtoId<LocalizedDatasetPrototype> PossibleLastEnemyNames = "SpaceVillainNamesEnemyLast";
|
||||
|
||||
/// <summary>
|
||||
/// The prototypes that can be dispensed as a reward for winning the game.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public List<EntProtoId> PossibleRewards = new();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum number of prizes the arcade machine can have.
|
||||
/// </summary>
|
||||
[DataField("rewardMinAmount")]
|
||||
[DataField]
|
||||
public int RewardMinAmount;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of prizes the arcade machine can have.
|
||||
/// </summary>
|
||||
[DataField("rewardMaxAmount")]
|
||||
[DataField]
|
||||
public int RewardMaxAmount;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,15 +4,18 @@ using Content.Server.Advertise.EntitySystems;
|
||||
using Content.Shared.Advertise.Components;
|
||||
using Content.Shared.Arcade;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Arcade.SpaceVillain;
|
||||
|
||||
public sealed partial class SpaceVillainArcadeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
@@ -52,7 +55,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
|
||||
/// <returns>A fight-verb.</returns>
|
||||
public string GenerateFightVerb(SpaceVillainArcadeComponent arcade)
|
||||
{
|
||||
return _random.Pick(arcade.PossibleFightVerbs);
|
||||
return _random.Pick(_prototypeManager.Index(arcade.PossibleFightVerbs));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,7 +64,10 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
|
||||
/// <returns>An enemy-name.</returns>
|
||||
public string GenerateEnemyName(SpaceVillainArcadeComponent arcade)
|
||||
{
|
||||
return $"{_random.Pick(arcade.PossibleFirstEnemyNames)} {_random.Pick(arcade.PossibleLastEnemyNames)}";
|
||||
var possibleFirstEnemyNames = _prototypeManager.Index(arcade.PossibleFirstEnemyNames);
|
||||
var possibleLastEnemyNames = _prototypeManager.Index(arcade.PossibleLastEnemyNames);
|
||||
|
||||
return $"{_random.Pick(possibleFirstEnemyNames)} {_random.Pick(possibleLastEnemyNames)}";
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, SpaceVillainArcadeComponent component, ComponentInit args)
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public void InvalidatePosition(Entity<MapGridComponent?> grid, Vector2i pos)
|
||||
{
|
||||
var query = GetEntityQuery<AirtightComponent>();
|
||||
_explosionSystem.UpdateAirtightMap(grid, pos, grid, query);
|
||||
_explosionSystem.UpdateAirtightMap(grid, pos, grid);
|
||||
_atmosphereSystem.InvalidateTile(grid.Owner, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Emp;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
using Content.Shared.Emp;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Clothing.Systems;
|
||||
|
||||
@@ -16,15 +12,12 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, ChameleonPrototypeSelectedMessage>(OnSelected);
|
||||
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, ChameleonClothingComponent component, MapInitEvent args)
|
||||
@@ -37,21 +30,6 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
||||
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)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
@@ -64,7 +42,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
||||
/// <summary>
|
||||
/// Change chameleon items name, description and sprite to mimic other entity prototype.
|
||||
/// </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)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
@@ -88,14 +66,6 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
||||
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)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -106,7 +76,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
|
||||
if (!chameleon.EmpContinuous)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < chameleon.NextEmpChange)
|
||||
if (Timing.CurTime < chameleon.NextEmpChange)
|
||||
continue;
|
||||
|
||||
// randomly pick cloth element from available and apply it
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Server.Mech.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Mech.Components;
|
||||
using Content.Shared.Power.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Medical;
|
||||
using Content.Shared.Medical;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
|
||||
|
||||
@@ -361,14 +361,11 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
if (hasLinking && HasComp<DeviceListComponent>(target) || hasLinking == configurator.LinkModeActive)
|
||||
return;
|
||||
|
||||
if (hasLinking)
|
||||
{
|
||||
SetMode(configuratorUid, configurator, userUid, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<DeviceNetworkComponent>(target))
|
||||
var hasNetworking = HasComp<DeviceNetworkComponent>(target);
|
||||
if (hasNetworking)
|
||||
SetMode(configuratorUid, configurator, userUid, false);
|
||||
else if (hasLinking)
|
||||
SetMode(configuratorUid, configurator, userUid, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -2,16 +2,11 @@ using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Radio;
|
||||
using Content.Server.SurveillanceCamera;
|
||||
using Content.Shared.Emp;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Emp;
|
||||
|
||||
public sealed class EmpSystem : SharedEmpSystem
|
||||
{
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
public const string EmpPulseEffectPrototype = "EffectEmpPulse";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -22,84 +17,6 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
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)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
@@ -120,14 +37,3 @@ public sealed class EmpSystem : SharedEmpSystem
|
||||
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.Fluids.EntitySystems;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Medical;
|
||||
using Content.Server.Polymorph.Components;
|
||||
using Content.Server.Polymorph.Systems;
|
||||
using Content.Server.Speech.Components;
|
||||
@@ -29,6 +28,7 @@ using Content.Shared.EntityEffects.Effects;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Medical;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Random;
|
||||
|
||||
@@ -17,8 +17,6 @@ using Content.Server.Info;
|
||||
using Content.Server.IoC;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Players.JobWhitelist;
|
||||
using Content.Server.Players.PlayTimeTracking;
|
||||
using Content.Server.Players.RateLimiting;
|
||||
@@ -44,102 +42,113 @@ namespace Content.Server.Entry
|
||||
internal const string ConfigPresetsDir = "/ConfigPresets/";
|
||||
private const string ConfigPresetsDirBuild = $"{ConfigPresetsDir}Build/";
|
||||
|
||||
private EuiManager _euiManager = default!;
|
||||
private IVoteManager _voteManager = default!;
|
||||
private ServerUpdateManager _updateManager = default!;
|
||||
private PlayTimeTrackingManager? _playTimeTracking;
|
||||
private IEntitySystemManager? _sysMan;
|
||||
private IServerDbManager? _dbManager;
|
||||
private IWatchlistWebhookManager _watchlistWebhookManager = default!;
|
||||
private IConnectionManager? _connectionManager;
|
||||
[Dependency] private readonly CVarControlManager _cvarCtrl = default!;
|
||||
[Dependency] private readonly ContentLocalizationManager _loc = default!;
|
||||
[Dependency] private readonly ContentNetworkResourceManager _netResMan = default!;
|
||||
[Dependency] private readonly DiscordChatLink _discordChatLink = default!;
|
||||
[Dependency] private readonly DiscordLink _discordLink = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
[Dependency] private readonly GhostKickManager _ghostKick = default!;
|
||||
[Dependency] private readonly IAdminManager _admin = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly IAfkManager _afk = default!;
|
||||
[Dependency] private readonly IBanManager _ban = default!;
|
||||
[Dependency] private readonly IChatManager _chatSan = default!;
|
||||
[Dependency] private readonly IChatSanitizationManager _chat = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IConnectionManager _connection = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entSys = default!;
|
||||
[Dependency] private readonly IGameMapManager _gameMap = default!;
|
||||
[Dependency] private readonly ILogManager _log = default!;
|
||||
[Dependency] private readonly INodeGroupFactory _nodeFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IResourceManager _res = default!;
|
||||
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||
[Dependency] private readonly IServerPreferencesManager _preferences = default!;
|
||||
[Dependency] private readonly IStatusHost _host = default!;
|
||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||
[Dependency] private readonly IWatchlistWebhookManager _watchlistWebhookManager = default!;
|
||||
[Dependency] private readonly JobWhitelistManager _job = default!;
|
||||
[Dependency] private readonly MultiServerKickManager _multiServerKick = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!;
|
||||
[Dependency] private readonly PlayerRateLimitManager _rateLimit = default!;
|
||||
[Dependency] private readonly RecipeManager _recipe = default!;
|
||||
[Dependency] private readonly RulesManager _rules = default!;
|
||||
[Dependency] private readonly ServerApi _serverApi = default!;
|
||||
[Dependency] private readonly ServerInfoManager _serverInfo = default!;
|
||||
[Dependency] private readonly ServerUpdateManager _updateManager = default!;
|
||||
|
||||
public override void PreInit()
|
||||
{
|
||||
ServerContentIoC.Register(Dependencies);
|
||||
foreach (var callback in TestingCallbacks)
|
||||
{
|
||||
var cast = (ServerModuleTestingCallbacks)callback;
|
||||
cast.ServerBeforeIoC?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
Dependencies.BuildGraph();
|
||||
Dependencies.InjectDependencies(this);
|
||||
|
||||
var cfg = IoCManager.Resolve<IConfigurationManager>();
|
||||
var res = IoCManager.Resolve<IResourceManager>();
|
||||
var logManager = IoCManager.Resolve<ILogManager>();
|
||||
LoadConfigPresets(_cfg, _res, _log.GetSawmill("configpreset"));
|
||||
|
||||
LoadConfigPresets(cfg, res, logManager.GetSawmill("configpreset"));
|
||||
var aczProvider = new ContentMagicAczProvider(Dependencies);
|
||||
_host.SetMagicAczProvider(aczProvider);
|
||||
|
||||
var aczProvider = new ContentMagicAczProvider(IoCManager.Resolve<IDependencyCollection>());
|
||||
IoCManager.Resolve<IStatusHost>().SetMagicAczProvider(aczProvider);
|
||||
_factory.DoAutoRegistrations();
|
||||
_factory.IgnoreMissingComponents("Visuals");
|
||||
_factory.RegisterIgnore(IgnoredComponents.List);
|
||||
_factory.GenerateNetIds();
|
||||
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
var prototypes = IoCManager.Resolve<IPrototypeManager>();
|
||||
_proto.RegisterIgnore("parallax");
|
||||
|
||||
factory.DoAutoRegistrations();
|
||||
factory.IgnoreMissingComponents("Visuals");
|
||||
_loc.Initialize();
|
||||
|
||||
factory.RegisterIgnore(IgnoredComponents.List);
|
||||
var dest = _cfg.GetCVar(CCVars.DestinationFile);
|
||||
if (!string.IsNullOrEmpty(dest))
|
||||
return; //hacky but it keeps load times for the generator down.
|
||||
|
||||
prototypes.RegisterIgnore("parallax");
|
||||
_log.GetSawmill("Storage").Level = LogLevel.Info;
|
||||
_log.GetSawmill("db.ef").Level = LogLevel.Info;
|
||||
|
||||
ServerContentIoC.Register();
|
||||
|
||||
foreach (var callback in TestingCallbacks)
|
||||
{
|
||||
var cast = (ServerModuleTestingCallbacks) callback;
|
||||
cast.ServerBeforeIoC?.Invoke();
|
||||
}
|
||||
|
||||
IoCManager.BuildGraph();
|
||||
factory.GenerateNetIds();
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
var dest = configManager.GetCVar(CCVars.DestinationFile);
|
||||
IoCManager.Resolve<ContentLocalizationManager>().Initialize();
|
||||
if (string.IsNullOrEmpty(dest)) //hacky but it keeps load times for the generator down.
|
||||
{
|
||||
_euiManager = IoCManager.Resolve<EuiManager>();
|
||||
_voteManager = IoCManager.Resolve<IVoteManager>();
|
||||
_updateManager = IoCManager.Resolve<ServerUpdateManager>();
|
||||
_playTimeTracking = IoCManager.Resolve<PlayTimeTrackingManager>();
|
||||
_connectionManager = IoCManager.Resolve<IConnectionManager>();
|
||||
_sysMan = IoCManager.Resolve<IEntitySystemManager>();
|
||||
_dbManager = IoCManager.Resolve<IServerDbManager>();
|
||||
_watchlistWebhookManager = IoCManager.Resolve<IWatchlistWebhookManager>();
|
||||
|
||||
logManager.GetSawmill("Storage").Level = LogLevel.Info;
|
||||
logManager.GetSawmill("db.ef").Level = LogLevel.Info;
|
||||
|
||||
IoCManager.Resolve<IAdminLogManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().Initialize();
|
||||
_dbManager.Init();
|
||||
IoCManager.Resolve<IServerPreferencesManager>().Init();
|
||||
IoCManager.Resolve<INodeGroupFactory>().Initialize();
|
||||
IoCManager.Resolve<ContentNetworkResourceManager>().Initialize();
|
||||
IoCManager.Resolve<GhostKickManager>().Initialize();
|
||||
_adminLog.Initialize();
|
||||
_connection.Initialize();
|
||||
_dbManager.Init();
|
||||
_preferences.Init();
|
||||
_nodeFactory.Initialize();
|
||||
_netResMan.Initialize();
|
||||
_ghostKick.Initialize();
|
||||
_serverInfo.Initialize();
|
||||
_serverApi.Initialize();
|
||||
_voteManager.Initialize();
|
||||
_updateManager.Initialize();
|
||||
_playTimeTracking.Initialize();
|
||||
_watchlistWebhookManager.Initialize();
|
||||
_job.Initialize();
|
||||
_rateLimit.Initialize();
|
||||
IoCManager.Resolve<TTSManager>().Initialize(); // Corvax-TTS
|
||||
IoCManager.Resolve<ServerInfoManager>().Initialize();
|
||||
IoCManager.Resolve<ServerApi>().Initialize();
|
||||
|
||||
_voteManager.Initialize();
|
||||
_updateManager.Initialize();
|
||||
_playTimeTracking.Initialize();
|
||||
_watchlistWebhookManager.Initialize();
|
||||
IoCManager.Resolve<JobWhitelistManager>().Initialize();
|
||||
IoCManager.Resolve<PlayerRateLimitManager>().Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostInit()
|
||||
{
|
||||
base.PostInit();
|
||||
|
||||
IoCManager.Resolve<IChatSanitizationManager>().Initialize();
|
||||
IoCManager.Resolve<IChatManager>().Initialize();
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
var resourceManager = IoCManager.Resolve<IResourceManager>();
|
||||
var dest = configManager.GetCVar(CCVars.DestinationFile);
|
||||
_chatSan.Initialize();
|
||||
_chat.Initialize();
|
||||
var dest = _cfg.GetCVar(CCVars.DestinationFile);
|
||||
if (!string.IsNullOrEmpty(dest))
|
||||
{
|
||||
var resPath = new ResPath(dest).ToRootedPath();
|
||||
var file = resourceManager.UserData.OpenWriteText(resPath.WithName("chem_" + dest));
|
||||
var file = _res.UserData.OpenWriteText(resPath.WithName("chem_" + dest));
|
||||
ChemistryJsonGenerator.PublishJson(file);
|
||||
file.Flush();
|
||||
file = resourceManager.UserData.OpenWriteText(resPath.WithName("react_" + dest));
|
||||
file = _res.UserData.OpenWriteText(resPath.WithName("react_" + dest));
|
||||
ReactionJsonGenerator.PublishJson(file);
|
||||
file.Flush();
|
||||
// Corvax-Wiki-Start
|
||||
@@ -153,27 +162,23 @@ namespace Content.Server.Entry
|
||||
HealthChangeReagentsJsonGenerator.PublishJson(file);
|
||||
file.Flush();
|
||||
// Corvax-Wiki-End
|
||||
IoCManager.Resolve<IBaseServer>().Shutdown("Data generation done");
|
||||
Dependencies.Resolve<IBaseServer>().Shutdown("Data generation done");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
IoCManager.Resolve<RecipeManager>().Initialize();
|
||||
IoCManager.Resolve<IAdminManager>().Initialize();
|
||||
IoCManager.Resolve<IAfkManager>().Initialize();
|
||||
IoCManager.Resolve<RulesManager>().Initialize();
|
||||
|
||||
IoCManager.Resolve<DiscordLink>().Initialize();
|
||||
IoCManager.Resolve<DiscordChatLink>().Initialize();
|
||||
|
||||
_euiManager.Initialize();
|
||||
|
||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
|
||||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().PostInit();
|
||||
IoCManager.Resolve<MultiServerKickManager>().Initialize();
|
||||
IoCManager.Resolve<CVarControlManager>().Initialize();
|
||||
}
|
||||
_recipe.Initialize();
|
||||
_admin.Initialize();
|
||||
_afk.Initialize();
|
||||
_rules.Initialize();
|
||||
_discordLink.Initialize();
|
||||
_discordChatLink.Initialize();
|
||||
_euiManager.Initialize();
|
||||
_gameMap.Initialize();
|
||||
_entSys.GetEntitySystem<GameTicker>().PostInitialize();
|
||||
_ban.Initialize();
|
||||
_connection.PostInit();
|
||||
_multiServerKick.Initialize();
|
||||
_cvarCtrl.Initialize();
|
||||
}
|
||||
|
||||
public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs)
|
||||
@@ -191,21 +196,27 @@ namespace Content.Server.Entry
|
||||
|
||||
case ModUpdateLevel.FramePostEngine:
|
||||
_updateManager.Update();
|
||||
_playTimeTracking?.Update();
|
||||
_playTimeTracking.Update();
|
||||
_watchlistWebhookManager.Update();
|
||||
_connectionManager?.Update();
|
||||
_connection.Update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_playTimeTracking?.Shutdown();
|
||||
_dbManager?.Shutdown();
|
||||
IoCManager.Resolve<ServerApi>().Shutdown();
|
||||
var dest = _cfg.GetCVar(CCVars.DestinationFile);
|
||||
if (!string.IsNullOrEmpty(dest))
|
||||
{
|
||||
_playTimeTracking.Shutdown();
|
||||
_dbManager.Shutdown();
|
||||
}
|
||||
|
||||
IoCManager.Resolve<DiscordLink>().Shutdown();
|
||||
IoCManager.Resolve<DiscordChatLink>().Shutdown();
|
||||
_serverApi.Shutdown();
|
||||
|
||||
// TODO Should this be awaited?
|
||||
_discordLink.Shutdown();
|
||||
_discordChatLink.Shutdown();
|
||||
}
|
||||
|
||||
private static void LoadConfigPresets(IConfigurationManager cfg, IResourceManager res, ISawmill sawmill)
|
||||
|
||||
14
Content.Server/ExCable/ExCableNodeGroup.cs
Normal file
14
Content.Server/ExCable/ExCableNodeGroup.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Shared.NodeContainer.NodeGroups;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
|
||||
namespace Content.Server.ExCable;
|
||||
|
||||
/// <summary>
|
||||
/// Dummy Node group class for handling the explosive cables.
|
||||
/// </summary>
|
||||
[NodeGroup(NodeGroupID.ExCable)]
|
||||
public sealed class ExCableNodeGroup : BaseNodeGroup
|
||||
{
|
||||
}
|
||||
@@ -11,8 +11,6 @@ namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed partial class ExplosionSystem
|
||||
{
|
||||
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
|
||||
|
||||
private readonly Dictionary<string, int> _explosionTypes = new();
|
||||
|
||||
private void InitAirtightMap()
|
||||
@@ -26,6 +24,8 @@ public sealed partial class ExplosionSystem
|
||||
int index = 0;
|
||||
foreach (var prototype in _prototypeManager.EnumeratePrototypes<ExplosionPrototype>())
|
||||
{
|
||||
// TODO EXPLOSION
|
||||
// just make this a field on the prototype
|
||||
_explosionTypes.Add(prototype.ID, index);
|
||||
index++;
|
||||
}
|
||||
@@ -42,10 +42,10 @@ public sealed partial class ExplosionSystem
|
||||
// indices to this tile-data struct.
|
||||
private Dictionary<EntityUid, Dictionary<Vector2i, TileData>> _airtightMap = new();
|
||||
|
||||
public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null, EntityQuery<AirtightComponent>? query = null)
|
||||
public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null)
|
||||
{
|
||||
if (Resolve(gridId, ref grid, false))
|
||||
UpdateAirtightMap(gridId, grid, tile, query);
|
||||
UpdateAirtightMap(gridId, grid, tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,7 +58,7 @@ public sealed partial class ExplosionSystem
|
||||
/// something like a normal and a reinforced windoor on the same tile. But given that this is a pretty rare
|
||||
/// occurrence, I am fine with this.
|
||||
/// </remarks>
|
||||
public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile, EntityQuery<AirtightComponent>? query = null)
|
||||
public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile)
|
||||
{
|
||||
var tolerance = new float[_explosionTypes.Count];
|
||||
var blockedDirections = AtmosDirection.Invalid;
|
||||
@@ -66,18 +66,15 @@ public sealed partial class ExplosionSystem
|
||||
if (!_airtightMap.ContainsKey(gridId))
|
||||
_airtightMap[gridId] = new();
|
||||
|
||||
query ??= GetEntityQuery<AirtightComponent>();
|
||||
var damageQuery = GetEntityQuery<DamageableComponent>();
|
||||
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
||||
var anchoredEnumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
|
||||
var anchoredEnumerator = _map.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
|
||||
|
||||
while (anchoredEnumerator.MoveNext(out var uid))
|
||||
{
|
||||
if (!query.Value.TryGetComponent(uid, out var airtight) || !airtight.AirBlocked)
|
||||
if (!_airtightQuery.TryGetComponent(uid, out var airtight) || !airtight.AirBlocked)
|
||||
continue;
|
||||
|
||||
blockedDirections |= airtight.AirBlockedDirection;
|
||||
var entityTolerances = GetExplosionTolerance(uid.Value, damageQuery, destructibleQuery);
|
||||
var entityTolerances = GetExplosionTolerance(uid.Value);
|
||||
for (var i = 0; i < tolerance.Length; i++)
|
||||
{
|
||||
tolerance[i] = Math.Max(tolerance[i], entityTolerances[i]);
|
||||
@@ -105,28 +102,25 @@ public sealed partial class ExplosionSystem
|
||||
if (!TryComp<MapGridComponent>(transform.GridUid, out var grid))
|
||||
return;
|
||||
|
||||
UpdateAirtightMap(transform.GridUid.Value, grid, _mapSystem.CoordinatesToTile(transform.GridUid.Value, grid, transform.Coordinates));
|
||||
UpdateAirtightMap(transform.GridUid.Value, grid, _map.CoordinatesToTile(transform.GridUid.Value, grid, transform.Coordinates));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a dictionary that specifies how intense a given explosion type needs to be in order to destroy an entity.
|
||||
/// </summary>
|
||||
public float[] GetExplosionTolerance(
|
||||
EntityUid uid,
|
||||
EntityQuery<DamageableComponent> damageQuery,
|
||||
EntityQuery<DestructibleComponent> destructibleQuery)
|
||||
public float[] GetExplosionTolerance(EntityUid uid)
|
||||
{
|
||||
// How much total damage is needed to destroy this entity? This also includes "break" behaviors. This ASSUMES
|
||||
// that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes
|
||||
// are currently effectively "invincible" as far as this is concerned. This really should be done more rigorously.
|
||||
var totalDamageTarget = FixedPoint2.MaxValue;
|
||||
if (destructibleQuery.TryGetComponent(uid, out var destructible))
|
||||
if (_destructibleQuery.TryGetComponent(uid, out var destructible))
|
||||
{
|
||||
totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible);
|
||||
}
|
||||
|
||||
var explosionTolerance = new float[_explosionTypes.Count];
|
||||
if (totalDamageTarget == FixedPoint2.MaxValue || !damageQuery.TryGetComponent(uid, out var damageable))
|
||||
if (totalDamageTarget == FixedPoint2.MaxValue || !_damageableQuery.TryGetComponent(uid, out var damageable))
|
||||
{
|
||||
for (var i = 0; i < explosionTolerance.Length; i++)
|
||||
{
|
||||
@@ -139,9 +133,12 @@ public sealed partial class ExplosionSystem
|
||||
// does not support entities dynamically changing explosive resistances (e.g. via clothing). But these probably
|
||||
// shouldn't be airtight structures anyways....
|
||||
|
||||
var mod = _damageableSystem.UniversalAllDamageModifier * _damageableSystem.UniversalExplosionDamageModifier;
|
||||
foreach (var (id, index) in _explosionTypes)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ExplosionPrototype>(id, out var explosionType))
|
||||
// TODO EXPLOSION SYSTEM
|
||||
// cache explosion type damage.
|
||||
if (!_prototypeManager.Resolve(id, out ExplosionPrototype? explosionType))
|
||||
continue;
|
||||
|
||||
// evaluate the damage that this damage type would do to this entity
|
||||
@@ -151,10 +148,15 @@ public sealed partial class ExplosionSystem
|
||||
if (!damageable.Damage.DamageDict.ContainsKey(type))
|
||||
continue;
|
||||
|
||||
// TODO EXPLOSION SYSTEM
|
||||
// add a variant of the event that gets raised once, instead of once per prototype.
|
||||
// Or better yet, just calculate this manually w/o the event.
|
||||
// The event mainly exists for indirect resistances via things like inventory & clothing
|
||||
// But this shouldn't matter for airtight entities.
|
||||
var ev = new GetExplosionResistanceEvent(explosionType.ID);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
damagePerIntensity += value * Math.Max(0, ev.DamageCoefficient);
|
||||
damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient);
|
||||
}
|
||||
|
||||
explosionTolerance[index] = damagePerIntensity > 0
|
||||
@@ -179,4 +181,16 @@ public sealed partial class ExplosionSystem
|
||||
public float[] ExplosionTolerance;
|
||||
public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
|
||||
}
|
||||
|
||||
public override void ReloadMap()
|
||||
{
|
||||
foreach (var(grid, dict) in _airtightMap)
|
||||
{
|
||||
var comp = Comp<MapGridComponent>(grid);
|
||||
foreach (var index in dict.Keys)
|
||||
{
|
||||
UpdateAirtightMap(grid, comp, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ public sealed partial class ExplosionSystem
|
||||
{
|
||||
var neighbourIndex = change.GridIndices + NeighbourVectors[i];
|
||||
|
||||
if (_mapSystem.TryGetTileRef(ev.Entity, grid, neighbourIndex, out var neighbourTile) && !neighbourTile.Tile.IsEmpty)
|
||||
if (_map.TryGetTileRef(ev.Entity, grid, neighbourIndex, out var neighbourTile) && !neighbourTile.Tile.IsEmpty)
|
||||
{
|
||||
var oppositeDirection = (NeighborFlag)(1 << ((i + 4) % 8));
|
||||
edges[neighbourIndex] = edges.GetValueOrDefault(neighbourIndex) | oppositeDirection;
|
||||
@@ -307,7 +307,7 @@ public sealed partial class ExplosionSystem
|
||||
spaceDirections = NeighborFlag.Invalid;
|
||||
for (var i = 0; i < NeighbourVectors.Length; i++)
|
||||
{
|
||||
if (!_mapSystem.TryGetTileRef(grid, grid.Comp, index + NeighbourVectors[i], out var neighborTile) || neighborTile.Tile.IsEmpty)
|
||||
if (!_map.TryGetTileRef(grid, grid.Comp, index + NeighbourVectors[i], out var neighborTile) || neighborTile.Tile.IsEmpty)
|
||||
spaceDirections |= (NeighborFlag) (1 << i);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ namespace Content.Server.Explosion.EntitySystems;
|
||||
|
||||
public sealed partial class ExplosionSystem
|
||||
{
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>.
|
||||
/// </summary>
|
||||
@@ -446,7 +444,7 @@ public sealed partial class ExplosionSystem
|
||||
GetEntitiesToDamage(uid, originalDamage, id);
|
||||
foreach (var (entity, damage) in _toDamage)
|
||||
{
|
||||
if (damage.GetTotal() > 0 && TryComp<ActorComponent>(entity, out var actorComponent))
|
||||
if (_actorQuery.HasComp(entity))
|
||||
{
|
||||
// Log damage to player entities only, cause this will create a massive amount of log spam otherwise.
|
||||
if (cause != null)
|
||||
@@ -461,7 +459,7 @@ public sealed partial class ExplosionSystem
|
||||
}
|
||||
|
||||
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
|
||||
_damageableSystem.TryChangeDamage(entity, damage * _damageableSystem.UniversalExplosionDamageModifier, ignoreResistances: true);
|
||||
_damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true, ignoreGlobalModifiers: true);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -668,6 +666,7 @@ sealed class Explosion
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly ExplosionSystem _system;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly DamageableSystem _damageable;
|
||||
|
||||
public readonly EntityUid VisualEnt;
|
||||
|
||||
@@ -688,10 +687,10 @@ sealed class Explosion
|
||||
int maxTileBreak,
|
||||
bool canCreateVacuum,
|
||||
IEntityManager entMan,
|
||||
IMapManager mapMan,
|
||||
EntityUid visualEnt,
|
||||
EntityUid? cause,
|
||||
SharedMapSystem mapSystem)
|
||||
SharedMapSystem mapSystem,
|
||||
DamageableSystem damageable)
|
||||
{
|
||||
VisualEnt = visualEnt;
|
||||
Cause = cause;
|
||||
@@ -706,6 +705,7 @@ sealed class Explosion
|
||||
_maxTileBreak = maxTileBreak;
|
||||
_canCreateVacuum = canCreateVacuum;
|
||||
_entMan = entMan;
|
||||
_damageable = damageable;
|
||||
|
||||
_xformQuery = entMan.GetEntityQuery<TransformComponent>();
|
||||
_physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
|
||||
@@ -760,8 +760,10 @@ sealed class Explosion
|
||||
_expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity;
|
||||
}
|
||||
#endif
|
||||
|
||||
_currentDamage = ExplosionType.DamagePerIntensity * _currentIntensity;
|
||||
var modifier = _currentIntensity
|
||||
* _damageable.UniversalExplosionDamageModifier
|
||||
* _damageable.UniversalAllDamageModifier;
|
||||
_currentDamage = ExplosionType.DamagePerIntensity * modifier;
|
||||
|
||||
// only throw if either the explosion is small, or if this is the outer ring of a large explosion.
|
||||
var doThrow = Area < _system.ThrowLimit || CurrentIteration > _tileSetIntensity.Count - 6;
|
||||
|
||||
@@ -52,7 +52,7 @@ public sealed partial class ExplosionSystem
|
||||
|
||||
// get the epicenter tile indices
|
||||
if (_mapManager.TryFindGridAt(epicenter, out var gridUid, out var candidateGrid) &&
|
||||
_mapSystem.TryGetTileRef(gridUid, candidateGrid, _mapSystem.WorldToTile(gridUid, candidateGrid, epicenter.Position), out var tileRef) &&
|
||||
_map.TryGetTileRef(gridUid, candidateGrid, _map.WorldToTile(gridUid, candidateGrid, epicenter.Position), out var tileRef) &&
|
||||
!tileRef.Tile.IsEmpty)
|
||||
{
|
||||
epicentreGrid = gridUid;
|
||||
@@ -62,7 +62,7 @@ public sealed partial class ExplosionSystem
|
||||
{
|
||||
// reference grid defines coordinate system that the explosion in space will use
|
||||
var gridComp = Comp<MapGridComponent>(referenceGrid.Value);
|
||||
initialTile = _mapSystem.WorldToTile(referenceGrid.Value, gridComp, epicenter.Position);
|
||||
initialTile = _map.WorldToTile(referenceGrid.Value, gridComp, epicenter.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Destructible;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NPC.Pathfinding;
|
||||
using Content.Shared.Atmos.Components;
|
||||
@@ -16,7 +18,6 @@ using Content.Shared.GameTicking;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
@@ -38,23 +39,28 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
|
||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
|
||||
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly PvsOverrideSystem _pvsSys = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
|
||||
|
||||
private EntityQuery<FlammableComponent> _flammableQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<ProjectileComponent> _projectileQuery;
|
||||
private EntityQuery<ActorComponent> _actorQuery;
|
||||
private EntityQuery<DestructibleComponent> _destructibleQuery;
|
||||
private EntityQuery<DamageableComponent> _damageableQuery;
|
||||
private EntityQuery<AirtightComponent> _airtightQuery;
|
||||
|
||||
/// <summary>
|
||||
/// "Tile-size" for space when there are no nearby grids to use as a reference.
|
||||
@@ -93,6 +99,10 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
_flammableQuery = GetEntityQuery<FlammableComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_projectileQuery = GetEntityQuery<ProjectileComponent>();
|
||||
_actorQuery = GetEntityQuery<ActorComponent>();
|
||||
_destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
||||
_damageableQuery = GetEntityQuery<DamageableComponent>();
|
||||
_airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||
}
|
||||
|
||||
private void OnReset(RoundRestartCleanupEvent ev)
|
||||
@@ -317,7 +327,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
private Explosion? SpawnExplosion(QueuedExplosion queued)
|
||||
{
|
||||
var pos = queued.Epicenter;
|
||||
if (!_mapSystem.MapExists(pos.MapId))
|
||||
if (!_map.MapExists(pos.MapId))
|
||||
return null;
|
||||
|
||||
var results = GetExplosionTiles(pos, queued.Proto.ID, queued.TotalIntensity, queued.Slope, queued.MaxTileIntensity);
|
||||
@@ -333,7 +343,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
|
||||
|
||||
//For whatever bloody reason, sound system requires ENTITY coordinates.
|
||||
var mapEntityCoords = _transformSystem.ToCoordinates(_mapSystem.GetMap(pos.MapId), pos);
|
||||
var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
|
||||
|
||||
// play sound.
|
||||
// for the normal audio, we want everyone in pvs range
|
||||
@@ -376,10 +386,10 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
queued.MaxTileBreak,
|
||||
queued.CanCreateVacuum,
|
||||
EntityManager,
|
||||
_mapManager,
|
||||
visualEnt,
|
||||
queued.Cause,
|
||||
_map);
|
||||
_map,
|
||||
_damageableSystem);
|
||||
}
|
||||
|
||||
private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Storage;
|
||||
|
||||
namespace Content.Server.Holosign;
|
||||
@@ -12,7 +12,6 @@ public sealed class HolosignSystem : EntitySystem
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Storage;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Interaction
|
||||
{
|
||||
// TODO Remove Shared prefix
|
||||
public sealed class InteractionSystem : SharedInteractionSystem;
|
||||
}
|
||||
|
||||
@@ -27,60 +27,60 @@ using Content.Server.Worldgen.Tools;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.IoC;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Players.RateLimiting;
|
||||
|
||||
namespace Content.Server.IoC
|
||||
{
|
||||
internal static class ServerContentIoC
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
IoCManager.Register<IChatManager, ChatManager>();
|
||||
IoCManager.Register<ISharedChatManager, ChatManager>();
|
||||
IoCManager.Register<IChatSanitizationManager, ChatSanitizationManager>();
|
||||
IoCManager.Register<IServerPreferencesManager, ServerPreferencesManager>();
|
||||
IoCManager.Register<IServerDbManager, ServerDbManager>();
|
||||
IoCManager.Register<RecipeManager, RecipeManager>();
|
||||
IoCManager.Register<INodeGroupFactory, NodeGroupFactory>();
|
||||
IoCManager.Register<IConnectionManager, ConnectionManager>();
|
||||
IoCManager.Register<ServerUpdateManager>();
|
||||
IoCManager.Register<IAdminManager, AdminManager>();
|
||||
IoCManager.Register<ISharedAdminManager, AdminManager>();
|
||||
IoCManager.Register<EuiManager, EuiManager>();
|
||||
IoCManager.Register<IVoteManager, VoteManager>();
|
||||
IoCManager.Register<IPlayerLocator, PlayerLocator>();
|
||||
IoCManager.Register<IAfkManager, AfkManager>();
|
||||
IoCManager.Register<IGameMapManager, GameMapManager>();
|
||||
IoCManager.Register<RulesManager, RulesManager>();
|
||||
IoCManager.Register<IBanManager, BanManager>();
|
||||
IoCManager.Register<ContentNetworkResourceManager>();
|
||||
IoCManager.Register<IAdminNotesManager, AdminNotesManager>();
|
||||
IoCManager.Register<GhostKickManager>();
|
||||
IoCManager.Register<ISharedAdminLogManager, AdminLogManager>();
|
||||
IoCManager.Register<IAdminLogManager, AdminLogManager>();
|
||||
IoCManager.Register<PlayTimeTrackingManager>();
|
||||
IoCManager.Register<UserDbDataManager>();
|
||||
IoCManager.Register<TTSManager>(); // Corvax-TTS
|
||||
IoCManager.Register<ServerInfoManager>();
|
||||
IoCManager.Register<PoissonDiskSampler>();
|
||||
IoCManager.Register<DiscordWebhook>();
|
||||
IoCManager.Register<VoteWebhooks>();
|
||||
IoCManager.Register<ServerDbEntryManager>();
|
||||
IoCManager.Register<ISharedPlaytimeManager, PlayTimeTrackingManager>();
|
||||
IoCManager.Register<ServerApi>();
|
||||
IoCManager.Register<JobWhitelistManager>();
|
||||
IoCManager.Register<PlayerRateLimitManager>();
|
||||
IoCManager.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
|
||||
IoCManager.Register<MappingManager>();
|
||||
IoCManager.Register<IWatchlistWebhookManager, WatchlistWebhookManager>();
|
||||
IoCManager.Register<ConnectionManager>();
|
||||
IoCManager.Register<MultiServerKickManager>();
|
||||
IoCManager.Register<CVarControlManager>();
|
||||
namespace Content.Server.IoC;
|
||||
|
||||
IoCManager.Register<DiscordLink>();
|
||||
IoCManager.Register<DiscordChatLink>();
|
||||
}
|
||||
internal static class ServerContentIoC
|
||||
{
|
||||
public static void Register(IDependencyCollection deps)
|
||||
{
|
||||
SharedContentIoC.Register(deps);
|
||||
deps.Register<IChatManager, ChatManager>();
|
||||
deps.Register<ISharedChatManager, ChatManager>();
|
||||
deps.Register<IChatSanitizationManager, ChatSanitizationManager>();
|
||||
deps.Register<IServerPreferencesManager, ServerPreferencesManager>();
|
||||
deps.Register<IServerDbManager, ServerDbManager>();
|
||||
deps.Register<RecipeManager, RecipeManager>();
|
||||
deps.Register<INodeGroupFactory, NodeGroupFactory>();
|
||||
deps.Register<IConnectionManager, ConnectionManager>();
|
||||
deps.Register<ServerUpdateManager>();
|
||||
deps.Register<IAdminManager, AdminManager>();
|
||||
deps.Register<ISharedAdminManager, AdminManager>();
|
||||
deps.Register<EuiManager, EuiManager>();
|
||||
deps.Register<IVoteManager, VoteManager>();
|
||||
deps.Register<IPlayerLocator, PlayerLocator>();
|
||||
deps.Register<IAfkManager, AfkManager>();
|
||||
deps.Register<IGameMapManager, GameMapManager>();
|
||||
deps.Register<RulesManager, RulesManager>();
|
||||
deps.Register<IBanManager, BanManager>();
|
||||
deps.Register<ContentNetworkResourceManager>();
|
||||
deps.Register<IAdminNotesManager, AdminNotesManager>();
|
||||
deps.Register<GhostKickManager>();
|
||||
deps.Register<ISharedAdminLogManager, AdminLogManager>();
|
||||
deps.Register<IAdminLogManager, AdminLogManager>();
|
||||
deps.Register<PlayTimeTrackingManager>();
|
||||
deps.Register<UserDbDataManager>();
|
||||
deps.Register<ServerInfoManager>();
|
||||
deps.Register<PoissonDiskSampler>();
|
||||
deps.Register<DiscordWebhook>();
|
||||
deps.Register<VoteWebhooks>();
|
||||
deps.Register<ServerDbEntryManager>();
|
||||
deps.Register<ISharedPlaytimeManager, PlayTimeTrackingManager>();
|
||||
deps.Register<ServerApi>();
|
||||
deps.Register<JobWhitelistManager>();
|
||||
deps.Register<PlayerRateLimitManager>();
|
||||
deps.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
|
||||
deps.Register<MappingManager>();
|
||||
deps.Register<IWatchlistWebhookManager, WatchlistWebhookManager>();
|
||||
deps.Register<ConnectionManager>();
|
||||
deps.Register<MultiServerKickManager>();
|
||||
deps.Register<CVarControlManager>();
|
||||
deps.Register<DiscordLink>();
|
||||
deps.Register<DiscordChatLink>();
|
||||
IoCManager.Register<TTSManager>(); // Corvax-TTS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Examine;
|
||||
using Content.Shared.Light;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Station.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Light.EntitySystems;
|
||||
@@ -16,8 +15,6 @@ public sealed class PoweredLightSystem : SharedPoweredLightSystem
|
||||
SubscribeLocalEvent<PoweredLightComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
|
||||
|
||||
SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
}
|
||||
|
||||
private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)
|
||||
@@ -55,10 +52,4 @@ public sealed class PoweredLightSystem : SharedPoweredLightSystem
|
||||
// need this to update visualizers
|
||||
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.Body.Systems;
|
||||
using Content.Server.Mech.Components;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Damage;
|
||||
@@ -14,6 +13,7 @@ using Content.Shared.Mech.Components;
|
||||
using Content.Shared.Mech.EntitySystems;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Tools;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Content.Shared.Tools.Systems;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Medical.CrewMonitoring;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Content.Shared.Medical.SuitSensors;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -14,14 +12,6 @@ public sealed class SuitSensorSystem : SharedSuitSensorSystem
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = 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)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -70,22 +60,4 @@ public sealed class SuitSensorSystem : SharedSuitSensorSystem
|
||||
_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.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Ninja.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Emp;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Content.Server.Ninja.Systems;
|
||||
/// </summary>
|
||||
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 SpaceNinjaSystem _ninja = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
@@ -30,7 +30,6 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, EmpAttemptEvent>(OnEmpAttempt);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
|
||||
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
|
||||
}
|
||||
@@ -44,7 +43,7 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
// raise event to let ninja components get starting battery
|
||||
_ninja.GetNinjaBattery(user.Owner, out var uid, out var _);
|
||||
|
||||
if (uid is not {} battery_uid)
|
||||
if (uid is not { } battery_uid)
|
||||
return;
|
||||
|
||||
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,
|
||||
// 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)
|
||||
return battcomp.MaxCharge + (selfcomp.AutoRechargeRate*AutoRechargeValue);
|
||||
return battcomp.MaxCharge + selfcomp.AutoRechargeRate * AutoRechargeValue;
|
||||
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)
|
||||
{
|
||||
base.UserUnequippedSuit(ent, user);
|
||||
@@ -144,6 +136,7 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
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)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
@@ -159,7 +152,6 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
|
||||
if (CheckDisabled(ent, user))
|
||||
return;
|
||||
|
||||
var coords = _transform.GetMapCoordinates(user);
|
||||
_emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration);
|
||||
_emp.EmpPulse(Transform(user).Coordinates, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration, user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.CriminalRecords.Systems;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Server.Research.Systems;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Ninja.Components;
|
||||
using Content.Shared.Ninja.Systems;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Ninja.Systems;
|
||||
|
||||
|
||||
@@ -447,6 +447,7 @@ namespace Content.Server.NodeContainer.EntitySystems
|
||||
NodeGroupID.Pipe => Color.Blue,
|
||||
NodeGroupID.WireNet => Color.DarkMagenta,
|
||||
NodeGroupID.Teg => Color.Red,
|
||||
NodeGroupID.ExCable => Color.Pink,
|
||||
_ => Color.White
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Physics.Controllers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
@@ -8,7 +9,7 @@ namespace Content.Server.Physics.Components;
|
||||
/// <summary>
|
||||
/// A component which makes its entity chasing entity with selected component.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(ChasingWalkSystem)), AutoGenerateComponentPause]
|
||||
[RegisterComponent, Access(typeof(ChasingWalkSystem), typeof(AdminVerbSystem)), AutoGenerateComponentPause]
|
||||
public sealed partial class ChasingWalkComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -78,4 +79,16 @@ public sealed partial class ChasingWalkComponent : Component
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityUid? ChasingEntity;
|
||||
|
||||
/// <summary>
|
||||
/// whether the entity should point in the direction its moving
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool RotateWithImpulse;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite rotation offset.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Angle RotationAngleOffset = Angle.Zero;
|
||||
}
|
||||
|
||||
@@ -101,5 +101,11 @@ public sealed class ChasingWalkSystem : VirtualController
|
||||
|
||||
_physics.SetLinearVelocity(uid, speed);
|
||||
_physics.SetBodyStatus(uid, physics, BodyStatus.InAir); //If this is not done, from the explosion up close, the tesla will "Fall" to the ground, and almost stop moving.
|
||||
|
||||
if (component.RotateWithImpulse)
|
||||
{
|
||||
var ang = speed.ToAngle() + Angle.FromDegrees(90); // we want "Up" to be forward, bullet convention.
|
||||
_transform.SetWorldRotation(uid, ang + component.RotationAngleOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Shared.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,16 +1,35 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class CablePlacerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The structure prototype for the cable coil to place.
|
||||
/// </summary>
|
||||
[DataField("cablePrototypeID", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? CablePrototypeId = "CableHV";
|
||||
|
||||
/// <summary>
|
||||
/// What kind of wire prevents placing this wire over it as CableType.
|
||||
/// </summary>
|
||||
[DataField("blockingWireType")]
|
||||
public CableType BlockingCableType = CableType.HighVoltage;
|
||||
|
||||
/// <summary>
|
||||
/// Blacklist for things the cable cannot be placed over. For things that arent cables with CableTypes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist Blacklist = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the placed cable should go over tiles or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool OverTile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Power.Components;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.APC;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Emp;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -203,6 +204,9 @@ public sealed class ApcSystem : EntitySystem
|
||||
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)
|
||||
{
|
||||
if (component.MainBreakerEnabled)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -10,7 +12,7 @@ using Robust.Shared.Timing;
|
||||
namespace Content.Server.Power.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BatterySystem : EntitySystem
|
||||
public sealed class BatterySystem : SharedBatterySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
@@ -22,7 +24,6 @@ namespace Content.Server.Power.EntitySystems
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
|
||||
SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
|
||||
SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
|
||||
SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
|
||||
SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
|
||||
|
||||
@@ -50,7 +51,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
if (effectiveMax == 0)
|
||||
effectiveMax = 1;
|
||||
var chargeFraction = batteryComponent.CurrentCharge / effectiveMax;
|
||||
var chargePercentRounded = (int) (chargeFraction * 100);
|
||||
var chargePercentRounded = (int)(chargeFraction * 100);
|
||||
args.PushMarkup(
|
||||
Loc.GetString(
|
||||
"examinable-battery-component-examine-detail",
|
||||
@@ -108,15 +109,6 @@ namespace Content.Server.Power.EntitySystems
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (args.ResidualValue == 0)
|
||||
@@ -131,7 +123,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
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)
|
||||
return 0;
|
||||
@@ -139,7 +131,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
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))
|
||||
return;
|
||||
@@ -174,7 +166,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
/// <summary>
|
||||
/// Changes the current battery charge by some value
|
||||
/// </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))
|
||||
return 0;
|
||||
@@ -190,10 +182,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
return delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity has a self recharge and puts it on cooldown if applicable.
|
||||
/// </summary>
|
||||
public void TrySetChargeCooldown(EntityUid uid, float value = -1)
|
||||
public override void TrySetChargeCooldown(EntityUid uid, float value = -1)
|
||||
{
|
||||
if (!TryComp<BatterySelfRechargerComponent>(uid, out var batteryself))
|
||||
return;
|
||||
@@ -228,7 +217,7 @@ namespace Content.Server.Power.EntitySystems
|
||||
/// <summary>
|
||||
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
|
||||
/// </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)
|
||||
return false;
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems;
|
||||
@@ -13,6 +14,7 @@ public sealed partial class CableSystem
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
private void InitializeCablePlacer()
|
||||
{
|
||||
@@ -35,12 +37,14 @@ public sealed partial class CableSystem
|
||||
var snapPos = _map.TileIndicesFor((gridUid, grid), args.ClickLocation);
|
||||
var tileDef = (ContentTileDefinition)_tileManager[_map.GetTileRef(gridUid, grid, snapPos).Tile.TypeId];
|
||||
|
||||
if (!tileDef.IsSubFloor || !tileDef.Sturdy)
|
||||
if ((!component.OverTile && !tileDef.IsSubFloor) || !tileDef.Sturdy)
|
||||
return;
|
||||
|
||||
|
||||
foreach (var anchored in _map.GetAnchoredEntities((gridUid, grid), snapPos))
|
||||
{
|
||||
if (_whitelistSystem.IsBlacklistPass(component.Blacklist, anchored))
|
||||
return;
|
||||
|
||||
if (TryComp<CableComponent>(anchored, out var wire) && wire.CableType == component.BlockingCableType)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Emp;
|
||||
using JetBrains.Annotations;
|
||||
@@ -15,7 +16,7 @@ using Content.Shared.Whitelist;
|
||||
namespace Content.Server.Power.EntitySystems;
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class ChargerSystem : EntitySystem
|
||||
public sealed class ChargerSystem : SharedChargerSystem
|
||||
{
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
@@ -25,6 +26,8 @@ internal sealed class ChargerSystem : EntitySystem
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChargerComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ChargerComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
SubscribeLocalEvent<ChargerComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||
@@ -32,8 +35,6 @@ internal sealed class ChargerSystem : EntitySystem
|
||||
SubscribeLocalEvent<ChargerComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
||||
SubscribeLocalEvent<ChargerComponent, InsertIntoEntityStorageAttemptEvent>(OnEntityStorageInsertAttempt);
|
||||
SubscribeLocalEvent<ChargerComponent, ExaminedEvent>(OnChargerExamine);
|
||||
|
||||
SubscribeLocalEvent<ChargerComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, ChargerComponent component, ComponentStartup args)
|
||||
@@ -46,7 +47,7 @@ internal sealed class ChargerSystem : EntitySystem
|
||||
using (args.PushGroup(nameof(ChargerComponent)))
|
||||
{
|
||||
// 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
|
||||
if (!_container.TryGetContainer(uid, component.SlotId, out var container))
|
||||
@@ -70,7 +71,7 @@ internal sealed class ChargerSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!component.Portable)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.Nodes;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
@@ -9,6 +7,7 @@ using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Content.Shared.Station.Components;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Rejuvenate;
|
||||
|
||||
namespace Content.Server.Power.EntitySystems;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.SMES;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
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.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.EntitySystems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
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.Components;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.UserInterface;
|
||||
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;
|
||||
|
||||
@@ -34,7 +34,6 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
||||
|
||||
SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
||||
SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(OnCellEmpAttempt);
|
||||
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
||||
@@ -221,14 +220,6 @@ public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
||||
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)
|
||||
{
|
||||
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.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.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
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Radio.Components;
|
||||
@@ -21,8 +20,6 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged);
|
||||
|
||||
SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak);
|
||||
|
||||
SubscribeLocalEvent<HeadsetComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
}
|
||||
|
||||
private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args)
|
||||
@@ -69,7 +66,6 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
protected override void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
||||
{
|
||||
base.OnGotUnequipped(uid, component, args);
|
||||
component.IsEquipped = false;
|
||||
RemComp<ActiveRadioComponent>(uid);
|
||||
RemComp<WearingHeadsetComponent>(args.Equipee);
|
||||
}
|
||||
@@ -82,6 +78,9 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
if (component.Enabled == value)
|
||||
return;
|
||||
|
||||
component.Enabled = value;
|
||||
Dirty(uid, component);
|
||||
|
||||
if (!value)
|
||||
{
|
||||
RemCompDeferred<ActiveRadioComponent>(uid);
|
||||
@@ -113,13 +112,4 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
|
||||
if (TryComp(parent, out ActorComponent? actor))
|
||||
_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.Shared.Power.Components;
|
||||
|
||||
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.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Power.Components;
|
||||
|
||||
namespace Content.Server.SensorMonitoring;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems;
|
||||
|
||||
public sealed partial class StorageSystem : SharedStorageSystem
|
||||
@@ -24,7 +16,6 @@ public sealed partial class StorageSystem : SharedStorageSystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<StorageComponent, BeforeExplodeEvent>(OnExploded);
|
||||
|
||||
SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ internal sealed class StunOnCollideSystem : EntitySystem
|
||||
if (ent.Comp.Refresh)
|
||||
{
|
||||
_stunSystem.TryUpdateStunDuration(target, ent.Comp.StunAmount);
|
||||
|
||||
_movementMod.TryUpdateMovementSpeedModDuration(
|
||||
target,
|
||||
MovementModStatusSystem.TaserSlowdown,
|
||||
|
||||
@@ -7,6 +7,8 @@ using Content.Shared.Examine;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
|
||||
namespace Content.Server.Stunnable.Systems
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.SurveillanceCamera.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Player;
|
||||
using static Content.Server.Chat.Systems.ChatSystem;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.SurveillanceCamera;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.SurveillanceCamera.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -15,7 +14,7 @@ using Content.Shared.DeviceNetwork.Components;
|
||||
|
||||
namespace Content.Server.SurveillanceCamera;
|
||||
|
||||
public sealed class SurveillanceCameraSystem : EntitySystem
|
||||
public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
@@ -57,15 +56,13 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SurveillanceCameraComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<SurveillanceCameraComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
SubscribeLocalEvent<SurveillanceCameraComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||
SubscribeLocalEvent<SurveillanceCameraComponent, SurveillanceCameraSetupSetName>(OnSetName);
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
SetActive(camera, args.Powered, component);
|
||||
@@ -173,6 +150,7 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
||||
|
||||
component.CameraId = args.Name;
|
||||
component.NameSet = true;
|
||||
Dirty(uid, component);
|
||||
UpdateSetupInterface(uid, component);
|
||||
_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);
|
||||
component.NetworkSet = true;
|
||||
Dirty(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))
|
||||
return;
|
||||
@@ -271,7 +250,7 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
||||
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))
|
||||
{
|
||||
@@ -418,21 +397,6 @@ public sealed class SurveillanceCameraSystem : EntitySystem
|
||||
|
||||
_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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Tesla.Components;
|
||||
using Content.Server.Lightning;
|
||||
using Content.Shared.Power.Components;
|
||||
|
||||
namespace Content.Server.Tesla.EntitySystems;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Vocalization.Systems;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Damage;
|
||||
@@ -35,7 +33,6 @@ namespace Content.Server.VendingMachines
|
||||
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
|
||||
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
|
||||
SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
|
||||
|
||||
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
||||
@@ -86,6 +83,7 @@ namespace Content.Server.VendingMachines
|
||||
private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, BreakageEventArgs eventArgs)
|
||||
{
|
||||
vendComponent.Broken = true;
|
||||
Dirty(uid, vendComponent);
|
||||
TryUpdateVisualState((uid, vendComponent));
|
||||
}
|
||||
|
||||
@@ -94,6 +92,7 @@ namespace Content.Server.VendingMachines
|
||||
if (!args.DamageIncreased && component.Broken)
|
||||
{
|
||||
component.Broken = false;
|
||||
Dirty(uid, component);
|
||||
TryUpdateVisualState((uid, component));
|
||||
return;
|
||||
}
|
||||
@@ -257,16 +256,6 @@ namespace Content.Server.VendingMachines
|
||||
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)
|
||||
{
|
||||
args.Cancelled |= ent.Comp.Broken;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Projectiles;
|
||||
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.
|
||||
/// </summary>
|
||||
[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.Xenoarchaeology.Artifact.XAE.Components;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Xenoarchaeology.Artifact;
|
||||
using Content.Shared.Xenoarchaeology.Artifact.XAE;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public sealed class XATMagnetSystem : BaseQueryUpdateXATSystem<XATMagnetComponen
|
||||
if (node.Attached == null)
|
||||
continue;
|
||||
|
||||
var artifact = _xenoArtifactQuery.Get(GetEntity(node.Attached.Value));
|
||||
var artifact = _xenoArtifactQuery.Get(node.Attached.Value);
|
||||
|
||||
if (!CanTrigger(artifact, (uid, node)))
|
||||
continue;
|
||||
|
||||
@@ -70,9 +70,10 @@ public sealed class AccessReaderSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
var examiner = args.Examiner;
|
||||
var canSeeAccessModification = accessHasBeenModified &&
|
||||
(HasComp<ShowAccessReaderSettingsComponent>(ent) ||
|
||||
_inventorySystem.TryGetInventoryEntity<ShowAccessReaderSettingsComponent>(args.Examiner, out _));
|
||||
(HasComp<ShowAccessReaderSettingsComponent>(examiner) ||
|
||||
_inventorySystem.TryGetInventoryEntity<ShowAccessReaderSettingsComponent>(examiner, out _));
|
||||
|
||||
if (canSeeAccessModification)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Content.Shared.Anomaly;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
|
||||
namespace Content.Server.Anomaly.Components;
|
||||
namespace Content.Shared.Anomaly.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for projectiles which affect anomalies through colliding with them.
|
||||
@@ -324,3 +324,10 @@ public readonly record struct AnomalyHealthChangedEvent(EntityUid Anomaly, float
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct AnomalyBehaviorChangedEvent(EntityUid Anomaly, ProtoId<AnomalyBehaviorPrototype>? Old, ProtoId<AnomalyBehaviorPrototype>? New);
|
||||
|
||||
/// <summary>
|
||||
/// Event of anomaly being affected by exotic particle.
|
||||
/// Is raised when particle collides with artifact.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AnomalyAffectedByParticleEvent(EntityUid Anomaly, EntityUid Particle);
|
||||
|
||||
@@ -61,5 +61,5 @@ public sealed partial class ElectricityAnomalyComponent : Component
|
||||
/// Duration of devices being disabled by the emp pulse upon going supercritical.
|
||||
/// <summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float EmpDisabledDuration = 60f;
|
||||
public TimeSpan EmpDisabledDuration = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Pointing;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Sound.Components;
|
||||
@@ -57,7 +58,7 @@ public sealed partial class SleepingSystem : EntitySystem
|
||||
SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<SleepingComponent, EntityZombifiedEvent>(OnZombified);
|
||||
SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SleepingComponent, ComponentInit>(OnCompInit);
|
||||
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
||||
SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
|
||||
SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt);
|
||||
@@ -68,6 +69,7 @@ public sealed partial class SleepingSystem : EntitySystem
|
||||
SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt);
|
||||
SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt);
|
||||
SubscribeLocalEvent<SleepingComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
|
||||
SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
|
||||
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
|
||||
@@ -133,7 +135,7 @@ public sealed partial class SleepingSystem : EntitySystem
|
||||
RemComp<SpamEmitSoundComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args)
|
||||
private void OnCompInit(Entity<SleepingComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
var ev = new SleepStateChangedEvent(true);
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
@@ -185,6 +187,11 @@ public sealed partial class SleepingSystem : EntitySystem
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnRejuvenate(Entity<SleepingComponent> ent, ref RejuvenateEvent args)
|
||||
{
|
||||
TryWaking((ent.Owner, ent.Comp), true);
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange)
|
||||
|
||||
@@ -2,13 +2,16 @@ using System.Linq;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Contraband;
|
||||
using Content.Shared.Emp;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -23,8 +26,11 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = 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 IRobustRandom _random = default!;
|
||||
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
||||
private static readonly SlotFlags[] IgnoredSlots =
|
||||
{
|
||||
@@ -32,12 +38,12 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
SlotFlags.PREVENTEQUIP,
|
||||
SlotFlags.NONE
|
||||
};
|
||||
|
||||
private static readonly SlotFlags[] Slots = Enum.GetValues<SlotFlags>().Except(IgnoredSlots).ToArray();
|
||||
|
||||
private readonly Dictionary<SlotFlags, List<EntProtoId>> _data = new();
|
||||
|
||||
public readonly Dictionary<SlotFlags, List<string>> ValidVariants = new();
|
||||
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
|
||||
|
||||
private static readonly ProtoId<TagPrototype> WhitelistChameleonTag = "WhitelistChameleon";
|
||||
|
||||
@@ -47,6 +53,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, GetVerbsEvent<InteractionVerb>>(OnVerb);
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, EmpPulseEvent>(OnEmpPulse);
|
||||
|
||||
SubscribeLocalEvent<ChameleonClothingComponent, PrototypesReloadedEventArgs>(OnPrototypeReload);
|
||||
PrepareAllVariants();
|
||||
@@ -97,21 +104,21 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
|
||||
// clothing sprite logic
|
||||
if (TryComp(uid, out ClothingComponent? clothing) &&
|
||||
proto.TryGetComponent("Clothing", out ClothingComponent? otherClothing))
|
||||
proto.TryGetComponent(out ClothingComponent? otherClothing, Factory))
|
||||
{
|
||||
_clothingSystem.CopyVisuals(uid, otherClothing, clothing);
|
||||
}
|
||||
|
||||
// appearance data logic
|
||||
if (TryComp(uid, out AppearanceComponent? appearance) &&
|
||||
proto.TryGetComponent("Appearance", out AppearanceComponent? appearanceOther))
|
||||
proto.TryGetComponent(out AppearanceComponent? appearanceOther, Factory))
|
||||
{
|
||||
_appearance.AppendData(appearanceOther, uid);
|
||||
Dirty(uid, appearance);
|
||||
}
|
||||
|
||||
// properly mark contraband
|
||||
if (proto.TryGetComponent("Contraband", out ContrabandComponent? contra))
|
||||
if (proto.TryGetComponent(out ContrabandComponent? contra, Factory))
|
||||
{
|
||||
EnsureComp<ContrabandComponent>(uid, out var 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) { }
|
||||
|
||||
/// <summary>
|
||||
@@ -157,7 +182,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
return false;
|
||||
|
||||
// check if it's valid clothing
|
||||
if (!proto.TryGetComponent("Clothing", out ClothingComponent? clothing))
|
||||
if (!proto.TryGetComponent(out ClothingComponent? clothing, Factory))
|
||||
return false;
|
||||
if (!clothing.Slots.HasFlag(chameleonSlot))
|
||||
return false;
|
||||
@@ -187,6 +212,14 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
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()
|
||||
{
|
||||
_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)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Shared.Damage
|
||||
/// <remarks>
|
||||
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
|
||||
/// </remarks>
|
||||
[DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier
|
||||
[DataField(readOnly: true)] // TODO FULL GAME SAVE
|
||||
public DamageSpecifier Damage = new();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,6 +20,11 @@ namespace Content.Shared.Damage
|
||||
[DataDefinition, Serializable, NetSerializable]
|
||||
public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier>
|
||||
{
|
||||
// For the record I regret so many of the decisions i made when rewriting damageable
|
||||
// Why is it just shitting out dictionaries left and right
|
||||
// One day Arrays, stackalloc spans, and SIMD will save the day.
|
||||
// TODO DAMAGEABLE REFACTOR
|
||||
|
||||
// These exist solely so the wiki works. Please do not touch them or use them.
|
||||
[JsonPropertyName("types")]
|
||||
[DataField("types", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, DamageTypePrototype>))]
|
||||
|
||||
@@ -2,9 +2,9 @@ using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Radiation.Events;
|
||||
@@ -25,10 +25,10 @@ namespace Content.Shared.Damage
|
||||
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly SharedChemistryGuideDataSystem _chemistryGuideData = default!;
|
||||
[Dependency] private readonly SharedExplosionSystem _explosion = default!;
|
||||
|
||||
private EntityQuery<AppearanceComponent> _appearanceQuery;
|
||||
private EntityQuery<DamageableComponent> _damageableQuery;
|
||||
private EntityQuery<MindContainerComponent> _mindContainerQuery;
|
||||
|
||||
public float UniversalAllDamageModifier { get; private set; } = 1f;
|
||||
public float UniversalAllHealModifier { get; private set; } = 1f;
|
||||
@@ -52,7 +52,6 @@ namespace Content.Shared.Damage
|
||||
|
||||
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
_damageableQuery = GetEntityQuery<DamageableComponent>();
|
||||
_mindContainerQuery = GetEntityQuery<MindContainerComponent>();
|
||||
|
||||
// Damage modifier CVars are updated and stored here to be queried in other systems.
|
||||
// Note that certain modifiers requires reloading the guidebook.
|
||||
@@ -60,6 +59,7 @@ namespace Content.Shared.Damage
|
||||
{
|
||||
UniversalAllDamageModifier = value;
|
||||
_chemistryGuideData.ReloadAllReagentPrototypes();
|
||||
_explosion.ReloadMap();
|
||||
}, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestAllHealModifier, value =>
|
||||
{
|
||||
@@ -80,7 +80,11 @@ namespace Content.Shared.Damage
|
||||
UniversalReagentHealModifier = value;
|
||||
_chemistryGuideData.ReloadAllReagentPrototypes();
|
||||
}, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestExplosionDamageModifier, value => UniversalExplosionDamageModifier = value, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestExplosionDamageModifier, value =>
|
||||
{
|
||||
UniversalExplosionDamageModifier = value;
|
||||
_explosion.ReloadMap();
|
||||
}, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestThrownDamageModifier, value => UniversalThrownDamageModifier = value, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestTopicalsHealModifier, value => UniversalTopicalsHealModifier = value, true);
|
||||
Subs.CVar(_config, CCVars.PlaytestMobDamageModifier, value => UniversalMobDamageModifier = value, true);
|
||||
@@ -156,6 +160,9 @@ namespace Content.Shared.Damage
|
||||
var data = new DamageVisualizerGroupData(component.DamagePerGroup.Keys.ToList());
|
||||
_appearance.SetData(uid, DamageVisualizerKeys.DamageUpdateGroups, data, appearance);
|
||||
}
|
||||
|
||||
// TODO DAMAGE
|
||||
// byref struct event.
|
||||
RaiseLocalEvent(uid, new DamageChangedEvent(component, damageDelta, interruptsDoAfters, origin));
|
||||
}
|
||||
|
||||
@@ -171,12 +178,24 @@ namespace Content.Shared.Damage
|
||||
/// Returns a <see cref="DamageSpecifier"/> with information about the actual damage changes. This will be
|
||||
/// null if the user had no applicable components that can take damage.
|
||||
/// </returns>
|
||||
public DamageSpecifier? TryChangeDamage(EntityUid? uid, DamageSpecifier damage, bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true, DamageableComponent? damageable = null, EntityUid? origin = null)
|
||||
/// <param name="ignoreResistances">If true, this will ignore the entity's damage modifier (<see cref="DamageableComponent.DamageModifierSetId"/> and skip raising a <see cref="DamageModifyEvent"/>.</param>
|
||||
/// <param name="interruptsDoAfters">Whether the damage should cancel any damage sensitive do-afters</param>
|
||||
/// <param name="origin">The entity that is causing this damage</param>
|
||||
/// <param name="ignoreGlobalModifiers">If true, this will skip over applying the universal damage modifiers (see <see cref="ApplyUniversalAllModifiers"/>).</param>
|
||||
/// <returns></returns>
|
||||
public DamageSpecifier? TryChangeDamage(
|
||||
EntityUid? uid,
|
||||
DamageSpecifier damage,
|
||||
bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true,
|
||||
DamageableComponent? damageable = null,
|
||||
EntityUid? origin = null,
|
||||
bool ignoreGlobalModifiers = false)
|
||||
{
|
||||
if (!uid.HasValue || !_damageableQuery.Resolve(uid.Value, ref damageable, false))
|
||||
{
|
||||
// TODO BODY SYSTEM pass damage onto body system
|
||||
// BOBBY WHEN?
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -195,13 +214,13 @@ namespace Content.Shared.Damage
|
||||
if (!ignoreResistances)
|
||||
{
|
||||
if (damageable.DamageModifierSetId != null &&
|
||||
_prototypeManager.Resolve<DamageModifierSetPrototype>(damageable.DamageModifierSetId, out var modifierSet))
|
||||
_prototypeManager.Resolve(damageable.DamageModifierSetId, out var modifierSet))
|
||||
{
|
||||
// TODO DAMAGE PERFORMANCE
|
||||
// use a local private field instead of creating a new dictionary here..
|
||||
damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
|
||||
}
|
||||
|
||||
// TODO DAMAGE
|
||||
// byref struct event.
|
||||
var ev = new DamageModifyEvent(damage, origin);
|
||||
RaiseLocalEvent(uid.Value, ev);
|
||||
damage = ev.Damage;
|
||||
@@ -212,11 +231,9 @@ namespace Content.Shared.Damage
|
||||
}
|
||||
}
|
||||
|
||||
damage = ApplyUniversalAllModifiers(damage);
|
||||
if (!ignoreGlobalModifiers)
|
||||
damage = ApplyUniversalAllModifiers(damage);
|
||||
|
||||
// TODO DAMAGE PERFORMANCE
|
||||
// Consider using a local private field instead of creating a new dictionary here.
|
||||
// Would need to check that nothing ever tries to cache the delta.
|
||||
var delta = new DamageSpecifier();
|
||||
delta.DamageDict.EnsureCapacity(damage.DamageDict.Count);
|
||||
|
||||
|
||||
@@ -5,24 +5,30 @@ namespace Content.Shared.Emp;
|
||||
|
||||
/// <summary>
|
||||
/// While entity has this component it is "disabled" by EMP.
|
||||
/// Add desired behaviour in other systems
|
||||
/// Add desired behaviour in other systems.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
[Access(typeof(SharedEmpSystem))]
|
||||
public sealed partial class EmpDisabledComponent : Component
|
||||
{
|
||||
/// <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>
|
||||
[DataField("timeLeft", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoPausedField]
|
||||
public TimeSpan DisabledUntil;
|
||||
|
||||
[DataField("effectCoolDown"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float EffectCooldown = 3f;
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan DisabledUntil = TimeSpan.Zero;
|
||||
|
||||
/// <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>
|
||||
[AutoPausedField]
|
||||
public TimeSpan TargetTime = TimeSpan.Zero;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user