Electrocution. (#4958)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
This commit is contained in:
committed by
GitHub
parent
66a3d5bf29
commit
ed3bf94a3b
7
Content.Client/Electrocution/ElectrocutionSystem.cs
Normal file
7
Content.Client/Electrocution/ElectrocutionSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.Electrocution;
|
||||||
|
|
||||||
|
namespace Content.Client.Electrocution
|
||||||
|
{
|
||||||
|
public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
||||||
|
{ }
|
||||||
|
}
|
||||||
@@ -58,6 +58,9 @@ namespace Content.Client.Entry
|
|||||||
"FloorTile",
|
"FloorTile",
|
||||||
"ShuttleController",
|
"ShuttleController",
|
||||||
"HumanInventoryController",
|
"HumanInventoryController",
|
||||||
|
"RandomInsulation",
|
||||||
|
"Electrified",
|
||||||
|
"Electrocution",
|
||||||
"Pourable",
|
"Pourable",
|
||||||
"Paper",
|
"Paper",
|
||||||
"Write",
|
"Write",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.Electrocution;
|
||||||
|
using Content.Shared.Construction;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Construction.Completions
|
||||||
|
{
|
||||||
|
[DataDefinition]
|
||||||
|
public class AttemptElectrocute : IGraphAction
|
||||||
|
{
|
||||||
|
public async Task PerformAction(IEntity entity, IEntity? user)
|
||||||
|
{
|
||||||
|
if (user == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntitySystem.Get<ElectrocutionSystem>().TryDoElectrifiedAct(entity.Uid, user.Uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Content.Server/Content.Server.csproj.DotSettings
Normal file
2
Content.Server/Content.Server.csproj.DotSettings
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=electrocution_005Ccomponents/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Electrocution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Component for things that shock users on touch.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public class ElectrifiedComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Electrified";
|
||||||
|
|
||||||
|
[DataField("enabled")]
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
|
[DataField("onBump")]
|
||||||
|
public bool OnBump { get; set; } = true;
|
||||||
|
|
||||||
|
[DataField("onAttacked")]
|
||||||
|
public bool OnAttacked { get; set; } = true;
|
||||||
|
|
||||||
|
[DataField("noWindowInTile")]
|
||||||
|
public bool NoWindowInTile { get; set; } = false;
|
||||||
|
|
||||||
|
[DataField("onHandInteract")]
|
||||||
|
public bool OnHandInteract { get; set; } = true;
|
||||||
|
|
||||||
|
[DataField("requirePower")]
|
||||||
|
public bool RequirePower { get; } = true;
|
||||||
|
|
||||||
|
[DataField("highVoltageNode")]
|
||||||
|
public string? HighVoltageNode { get; }
|
||||||
|
|
||||||
|
[DataField("mediumVoltageNode")]
|
||||||
|
public string? MediumVoltageNode { get; }
|
||||||
|
|
||||||
|
[DataField("lowVoltageNode")]
|
||||||
|
public string? LowVoltageNode { get; }
|
||||||
|
|
||||||
|
[DataField("highVoltageDamageMultiplier")]
|
||||||
|
public float HighVoltageDamageMultiplier { get; } = 3f;
|
||||||
|
|
||||||
|
[DataField("highVoltageTimeMultiplier")]
|
||||||
|
public float HighVoltageTimeMultiplier { get; } = 1.5f;
|
||||||
|
|
||||||
|
[DataField("mediumVoltageDamageMultiplier")]
|
||||||
|
public float MediumVoltageDamageMultiplier { get; } = 2f;
|
||||||
|
|
||||||
|
[DataField("mediumVoltageTimeMultiplier")]
|
||||||
|
public float MediumVoltageTimeMultiplier { get; } = 1.25f;
|
||||||
|
|
||||||
|
[DataField("shockDamage")]
|
||||||
|
public int ShockDamage { get; } = 20;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shock time, in seconds.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("shockTime")]
|
||||||
|
public float ShockTime { get; } = 30f;
|
||||||
|
|
||||||
|
[DataField("siemensCoefficient")]
|
||||||
|
public float SiemensCoefficient { get; } = 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Electrocution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Component for virtual electrocution entities (representing an in-progress shock).
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Friend(typeof(ElectrocutionSystem))]
|
||||||
|
public sealed class ElectrocutionComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Electrocution";
|
||||||
|
|
||||||
|
[DataField("timeLeft")] public float TimeLeft { get; set; }
|
||||||
|
[DataField("electrocuting")] public EntityUid Electrocuting { get; set; }
|
||||||
|
[DataField("accumDamage")] public float AccumulatedDamage { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Content.Server/Electrocution/ElectrocutionNode.cs
Normal file
29
Content.Server/Electrocution/ElectrocutionNode.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.NodeContainer;
|
||||||
|
using Content.Server.NodeContainer.Nodes;
|
||||||
|
using Content.Server.Power.Nodes;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Electrocution
|
||||||
|
{
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class ElectrocutionNode : Node
|
||||||
|
{
|
||||||
|
[DataField("cable")]
|
||||||
|
public EntityUid CableEntity;
|
||||||
|
[DataField("node")]
|
||||||
|
public string NodeName = default!;
|
||||||
|
|
||||||
|
public override IEnumerable<Node> GetReachableNodes()
|
||||||
|
{
|
||||||
|
var ent = IoCManager.Resolve<IEntityManager>();
|
||||||
|
if (!ent.TryGetComponent(CableEntity, out NodeContainerComponent? nodeContainer))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (nodeContainer.TryGetNode(NodeName, out Node? node))
|
||||||
|
yield return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
407
Content.Server/Electrocution/ElectrocutionSystem.cs
Normal file
407
Content.Server/Electrocution/ElectrocutionSystem.cs
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.NodeContainer;
|
||||||
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
|
using Content.Server.NodeContainer.Nodes;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.Power.NodeGroups;
|
||||||
|
using Content.Server.Window;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Prototypes;
|
||||||
|
using Content.Shared.Electrocution;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Jittering;
|
||||||
|
using Content.Shared.Maps;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Pulling.Components;
|
||||||
|
using Content.Shared.Speech.EntitySystems;
|
||||||
|
using Content.Shared.StatusEffect;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
using Content.Shared.Weapons.Melee;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Physics.Dynamics;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.Electrocution
|
||||||
|
{
|
||||||
|
public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityLookup _entityLookup = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||||
|
[Dependency] private readonly SharedJitteringSystem _jitteringSystem = default!;
|
||||||
|
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||||
|
[Dependency] private readonly SharedStutteringSystem _stutteringSystem = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
|
[Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
|
||||||
|
|
||||||
|
protected const string StatusEffectKey = "Electrocution";
|
||||||
|
protected const string DamageType = "Shock";
|
||||||
|
|
||||||
|
// Yes, this is absurdly small for a reason.
|
||||||
|
private const float ElectrifiedDamagePerWatt = 0.0015f;
|
||||||
|
|
||||||
|
private const float RecursiveDamageMultiplier = 0.75f;
|
||||||
|
private const float RecursiveTimeMultiplier = 0.8f;
|
||||||
|
|
||||||
|
private const float ParalyzeTimeMultiplier = 1f;
|
||||||
|
|
||||||
|
private const float StutteringTimeMultiplier = 1.5f;
|
||||||
|
|
||||||
|
private const float JitterTimeMultiplier = 0.75f;
|
||||||
|
private const float JitterAmplitude = 80f;
|
||||||
|
private const float JitterFrequency = 8f;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ElectrifiedComponent, StartCollideEvent>(OnElectrifiedStartCollide);
|
||||||
|
SubscribeLocalEvent<ElectrifiedComponent, AttackedEvent>(OnElectrifiedAttacked);
|
||||||
|
SubscribeLocalEvent<ElectrifiedComponent, InteractHandEvent>(OnElectrifiedHandInteract);
|
||||||
|
SubscribeLocalEvent<RandomInsulationComponent, MapInitEvent>(OnRandomInsulationMapInit);
|
||||||
|
|
||||||
|
UpdatesAfter.Add(typeof(PowerNetSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
// Update "in progress" electrocutions
|
||||||
|
|
||||||
|
RemQueue<ElectrocutionComponent> finishedElectrocutionsQueue = new();
|
||||||
|
foreach (var (electrocution, consumer) in EntityManager
|
||||||
|
.EntityQuery<ElectrocutionComponent, PowerConsumerComponent>())
|
||||||
|
{
|
||||||
|
var ftAdjusted = Math.Min(frameTime, electrocution.TimeLeft);
|
||||||
|
|
||||||
|
electrocution.TimeLeft -= ftAdjusted;
|
||||||
|
electrocution.AccumulatedDamage += consumer.ReceivedPower * ElectrifiedDamagePerWatt * ftAdjusted;
|
||||||
|
|
||||||
|
if (MathHelper.CloseTo(electrocution.TimeLeft, 0))
|
||||||
|
finishedElectrocutionsQueue.Add(electrocution);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var finished in finishedElectrocutionsQueue)
|
||||||
|
{
|
||||||
|
var uid = finished.Owner.Uid;
|
||||||
|
if (EntityManager.EntityExists(finished.Electrocuting))
|
||||||
|
{
|
||||||
|
// TODO: damage should be scaled by shock damage multiplier
|
||||||
|
// TODO: better paralyze/jitter timing
|
||||||
|
var damage = new DamageSpecifier(
|
||||||
|
_prototypeManager.Index<DamageTypePrototype>(DamageType),
|
||||||
|
(int) finished.AccumulatedDamage);
|
||||||
|
|
||||||
|
_damageableSystem.TryChangeDamage(finished.Electrocuting, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityManager.DeleteEntity(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnElectrifiedStartCollide(EntityUid uid, ElectrifiedComponent electrified, StartCollideEvent args)
|
||||||
|
{
|
||||||
|
if (!electrified.OnBump)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryDoElectrifiedAct(uid, args.OtherFixture.Body.Owner.Uid, electrified);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnElectrifiedAttacked(EntityUid uid, ElectrifiedComponent electrified, AttackedEvent args)
|
||||||
|
{
|
||||||
|
if (!electrified.OnAttacked)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryDoElectrifiedAct(uid, args.User.Uid, electrified);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnElectrifiedHandInteract(EntityUid uid, ElectrifiedComponent electrified, InteractHandEvent args)
|
||||||
|
{
|
||||||
|
if (!electrified.OnHandInteract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryDoElectrifiedAct(uid, args.User.Uid, electrified);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid,
|
||||||
|
ElectrifiedComponent? electrified = null,
|
||||||
|
NodeContainerComponent? nodeContainer = null,
|
||||||
|
ITransformComponent? transform = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref electrified, ref transform, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!electrified.Enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (electrified.NoWindowInTile)
|
||||||
|
{
|
||||||
|
foreach (var entity in transform.Coordinates.GetEntitiesInTile(
|
||||||
|
LookupFlags.Approximate | LookupFlags.IncludeAnchored, _entityLookup))
|
||||||
|
{
|
||||||
|
if (entity.HasComponent<WindowComponent>())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets = new List<(EntityUid entity, int depth)>();
|
||||||
|
GetChainedElectrocutionTargets(targetUid, targets);
|
||||||
|
if (!electrified.RequirePower)
|
||||||
|
{
|
||||||
|
var lastRet = true;
|
||||||
|
for (var i = targets.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var (entity, depth) = targets[i];
|
||||||
|
lastRet = TryDoElectrocution(
|
||||||
|
entity,
|
||||||
|
uid,
|
||||||
|
(int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth)),
|
||||||
|
TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)),
|
||||||
|
electrified.SiemensCoefficient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref nodeContainer, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var node = TryNode(electrified.HighVoltageNode) ??
|
||||||
|
TryNode(electrified.MediumVoltageNode) ??
|
||||||
|
TryNode(electrified.LowVoltageNode);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var (damageMult, timeMult) = node.NodeGroupID switch
|
||||||
|
{
|
||||||
|
NodeGroupID.HVPower => (electrified.HighVoltageDamageMultiplier, electrified.HighVoltageTimeMultiplier),
|
||||||
|
NodeGroupID.MVPower => (electrified.MediumVoltageDamageMultiplier,
|
||||||
|
electrified.MediumVoltageTimeMultiplier),
|
||||||
|
_ => (1f, 1f)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
var lastRet = true;
|
||||||
|
for (var i = targets.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var (entity, depth) = targets[i];
|
||||||
|
lastRet = TryDoElectrocutionPowered(
|
||||||
|
entity,
|
||||||
|
uid,
|
||||||
|
node,
|
||||||
|
(int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth) * damageMult),
|
||||||
|
TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth) *
|
||||||
|
timeMult),
|
||||||
|
electrified.SiemensCoefficient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node? TryNode(string? id)
|
||||||
|
{
|
||||||
|
if (id != null && nodeContainer.TryGetNode<Node>(id, out var node)
|
||||||
|
&& node.NodeGroup is IBasePowerNet { NetworkNode: { LastAvailableSupplySum: >0 } })
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>Whether the entity <see cref="uid"/> was stunned by the shock.</returns>
|
||||||
|
public bool TryDoElectrocution(
|
||||||
|
EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, float siemensCoefficient = 1f,
|
||||||
|
StatusEffectsComponent? statusEffects = null,
|
||||||
|
SharedAlertsComponent? alerts = null)
|
||||||
|
{
|
||||||
|
if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient)
|
||||||
|
|| !DoCommonElectrocution(uid, sourceUid, shockDamage, time, siemensCoefficient, statusEffects, alerts))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryDoElectrocutionPowered(
|
||||||
|
EntityUid uid,
|
||||||
|
EntityUid sourceUid,
|
||||||
|
Node node,
|
||||||
|
int shockDamage,
|
||||||
|
TimeSpan time,
|
||||||
|
float siemensCoefficient = 1f,
|
||||||
|
StatusEffectsComponent? statusEffects = null,
|
||||||
|
SharedAlertsComponent? alerts = null,
|
||||||
|
ITransformComponent? sourceTransform = null)
|
||||||
|
{
|
||||||
|
if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Coefficient needs to be higher than this to do a powered electrocution!
|
||||||
|
if(siemensCoefficient <= 0.5f)
|
||||||
|
return DoCommonElectrocution(uid, sourceUid, shockDamage, time, siemensCoefficient, statusEffects, alerts);
|
||||||
|
|
||||||
|
if (!DoCommonElectrocution(uid, sourceUid, null, time, siemensCoefficient, statusEffects, alerts))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!Resolve(sourceUid, ref sourceTransform)) // This shouldn't really happen, but just in case...
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var electrocutionEntity = EntityManager.SpawnEntity(
|
||||||
|
$"VirtualElectrocutionLoad{node.NodeGroupID}", sourceTransform.Coordinates);
|
||||||
|
|
||||||
|
var electrocutionNode = electrocutionEntity
|
||||||
|
.GetComponent<NodeContainerComponent>()
|
||||||
|
.GetNode<ElectrocutionNode>("electrocution");
|
||||||
|
|
||||||
|
var electrocutionComponent = electrocutionEntity.GetComponent<ElectrocutionComponent>();
|
||||||
|
|
||||||
|
electrocutionNode.CableEntity = sourceUid;
|
||||||
|
electrocutionNode.NodeName = node.Name;
|
||||||
|
|
||||||
|
_nodeGroupSystem.QueueReflood(electrocutionNode);
|
||||||
|
|
||||||
|
electrocutionComponent.TimeLeft = 1f;
|
||||||
|
electrocutionComponent.Electrocuting = uid;
|
||||||
|
|
||||||
|
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoCommonElectrocutionAttempt(EntityUid uid, EntityUid? sourceUid, ref float siemensCoefficient)
|
||||||
|
{
|
||||||
|
var attemptEvent = new ElectrocutionAttemptEvent(uid, sourceUid, siemensCoefficient);
|
||||||
|
RaiseLocalEvent(uid, attemptEvent);
|
||||||
|
|
||||||
|
// Cancel the electrocution early, so we don't recursively electrocute anything.
|
||||||
|
if (attemptEvent.Cancelled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
siemensCoefficient = attemptEvent.SiemensCoefficient;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid,
|
||||||
|
int? shockDamage, TimeSpan time, float siemensCoefficient = 1f,
|
||||||
|
StatusEffectsComponent? statusEffects = null,
|
||||||
|
SharedAlertsComponent? alerts = null)
|
||||||
|
{
|
||||||
|
if (siemensCoefficient <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (shockDamage != null)
|
||||||
|
{
|
||||||
|
shockDamage = (int) (shockDamage * siemensCoefficient);
|
||||||
|
|
||||||
|
if (shockDamage.Value <= 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional component.
|
||||||
|
Resolve(uid, ref alerts, false);
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref statusEffects, false) ||
|
||||||
|
!_statusEffectsSystem.CanApplyEffect(uid, StatusEffectKey, statusEffects))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_statusEffectsSystem.TryAddStatusEffect<ElectrocutedComponent>(uid, StatusEffectKey, time,
|
||||||
|
statusEffects, alerts))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var shouldStun = siemensCoefficient > 0.5f;
|
||||||
|
|
||||||
|
if (shouldStun)
|
||||||
|
_stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, statusEffects, alerts);
|
||||||
|
|
||||||
|
// TODO: Sparks here.
|
||||||
|
|
||||||
|
if(shockDamage is {} dmg)
|
||||||
|
_damageableSystem.TryChangeDamage(uid,
|
||||||
|
new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>(DamageType), dmg));
|
||||||
|
|
||||||
|
_stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, statusEffects, alerts);
|
||||||
|
_jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, JitterAmplitude, JitterFrequency, true,
|
||||||
|
statusEffects, alerts);
|
||||||
|
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid,
|
||||||
|
Filter.Entities(uid).Unpredicted());
|
||||||
|
|
||||||
|
var filter = Filter.Pvs(uid, 2f, EntityManager).RemoveWhereAttachedEntity(puid => puid == uid)
|
||||||
|
.Unpredicted();
|
||||||
|
|
||||||
|
// TODO: Allow being able to pass EntityUid to Loc...
|
||||||
|
if (sourceUid != null)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-by-source-popup-others",
|
||||||
|
("mob", EntityManager.GetEntity(uid)), ("source", EntityManager.GetEntity(sourceUid.Value))),
|
||||||
|
uid, filter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-others",
|
||||||
|
("mob", EntityManager.GetEntity(uid))), uid, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetChainedElectrocutionTargets(EntityUid source, List<(EntityUid entity, int depth)> all)
|
||||||
|
{
|
||||||
|
var visited = new HashSet<EntityUid>();
|
||||||
|
|
||||||
|
GetChainedElectrocutionTargetsRecurse(source, 1, visited, all);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetChainedElectrocutionTargetsRecurse(
|
||||||
|
EntityUid entity,
|
||||||
|
int depth,
|
||||||
|
HashSet<EntityUid> visited,
|
||||||
|
List<(EntityUid entity, int depth)> all)
|
||||||
|
{
|
||||||
|
all.Add((entity, depth));
|
||||||
|
visited.Add(entity);
|
||||||
|
|
||||||
|
if (EntityManager.TryGetComponent(entity, out SharedPullableComponent? pullable)
|
||||||
|
&& pullable.Puller != null
|
||||||
|
&& !visited.Contains(pullable.Puller.Uid))
|
||||||
|
{
|
||||||
|
GetChainedElectrocutionTargetsRecurse(pullable.Puller.Uid, depth + 1, visited, all);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EntityManager.TryGetComponent(entity, out SharedPullerComponent? puller)
|
||||||
|
&& puller.Pulling != null
|
||||||
|
&& !visited.Contains(puller.Pulling.Uid))
|
||||||
|
{
|
||||||
|
GetChainedElectrocutionTargetsRecurse(puller.Pulling.Uid, depth + 1, visited, all);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRandomInsulationMapInit(EntityUid uid, RandomInsulationComponent randomInsulation,
|
||||||
|
MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(uid, out InsulatedComponent? insulated))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (randomInsulation.List.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetInsulatedSiemensCoefficient(uid, _random.Pick(randomInsulation.List), insulated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Content.Server/Electrocution/RandomInsulationComponent.cs
Normal file
14
Content.Server/Electrocution/RandomInsulationComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Electrocution
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class RandomInsulationComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "RandomInsulation";
|
||||||
|
|
||||||
|
[DataField("list")]
|
||||||
|
public readonly float[] List = { 0f };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using Content.Server.Items;
|
|||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Slippery;
|
using Content.Shared.Slippery;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Electrocution;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ namespace Content.Server.Inventory
|
|||||||
SubscribeLocalEvent<InventoryComponent, HighPressureEvent>(OnHighPressureEvent);
|
SubscribeLocalEvent<InventoryComponent, HighPressureEvent>(OnHighPressureEvent);
|
||||||
SubscribeLocalEvent<InventoryComponent, LowPressureEvent>(OnLowPressureEvent);
|
SubscribeLocalEvent<InventoryComponent, LowPressureEvent>(OnLowPressureEvent);
|
||||||
SubscribeLocalEvent<InventoryComponent, DamageModifyEvent>(OnDamageModify);
|
SubscribeLocalEvent<InventoryComponent, DamageModifyEvent>(OnDamageModify);
|
||||||
|
SubscribeLocalEvent<InventoryComponent, ElectrocutionAttemptEvent>(OnElectrocutionAttempt);
|
||||||
SubscribeLocalEvent<InventoryComponent, SlipAttemptEvent>(OnSlipAttemptEvent);
|
SubscribeLocalEvent<InventoryComponent, SlipAttemptEvent>(OnSlipAttemptEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +53,14 @@ namespace Content.Server.Inventory
|
|||||||
RelayPressureEvent(component, args);
|
RelayPressureEvent(component, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnElectrocutionAttempt(EntityUid uid, InventoryComponent component, ElectrocutionAttemptEvent args)
|
||||||
|
{
|
||||||
|
foreach (var equipped in component.GetAllHeldItems())
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(equipped.Uid, args, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDamageModify(EntityUid uid, InventoryComponent component, DamageModifyEvent args)
|
private void OnDamageModify(EntityUid uid, InventoryComponent component, DamageModifyEvent args)
|
||||||
{
|
{
|
||||||
foreach (var equipped in component.GetAllHeldItems())
|
foreach (var equipped in component.GetAllHeldItems())
|
||||||
|
|||||||
@@ -8,7 +8,14 @@ using Robust.Shared.ViewVariables;
|
|||||||
|
|
||||||
namespace Content.Server.Power.Components
|
namespace Content.Server.Power.Components
|
||||||
{
|
{
|
||||||
public abstract class BaseNetConnectorComponent<TNetType> : Component
|
public interface IBaseNetConnectorComponent<in TNetType>
|
||||||
|
{
|
||||||
|
public TNetType? Net { set; }
|
||||||
|
public Voltage Voltage { get; }
|
||||||
|
public string? NodeId { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BaseNetConnectorComponent<TNetType> : Component, IBaseNetConnectorComponent<TNetType>
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public Voltage Voltage { get => _voltage; set => SetVoltage(value); }
|
public Voltage Voltage { get => _voltage; set => SetVoltage(value); }
|
||||||
@@ -22,7 +29,7 @@ namespace Content.Server.Power.Components
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private bool _needsNet => _net != null;
|
private bool _needsNet => _net != null;
|
||||||
|
|
||||||
[DataField("node")] [ViewVariables] public string? NodeId;
|
[DataField("node")] [ViewVariables] public string? NodeId { get; set; }
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ using Content.Server.Power.NodeGroups;
|
|||||||
|
|
||||||
namespace Content.Server.Power.Components
|
namespace Content.Server.Power.Components
|
||||||
{
|
{
|
||||||
|
public interface IBasePowerNetComponent : IBaseNetConnectorComponent<IPowerNet>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class BasePowerNetComponent : BaseNetConnectorComponent<IPowerNet>
|
public abstract class BasePowerNetComponent : BaseNetConnectorComponent<IPowerNet>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.Electrocution;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Server.Tools;
|
using Content.Server.Tools;
|
||||||
using Content.Server.Tools.Components;
|
using Content.Server.Tools.Components;
|
||||||
@@ -43,6 +44,8 @@ namespace Content.Server.Power.Components
|
|||||||
|
|
||||||
if (!await EntitySystem.Get<ToolSystem>().UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 0f, 0.25f, _cuttingQuality)) return false;
|
if (!await EntitySystem.Get<ToolSystem>().UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, 0f, 0.25f, _cuttingQuality)) return false;
|
||||||
|
|
||||||
|
if (EntitySystem.Get<ElectrocutionSystem>().TryDoElectrifiedAct(Owner.Uid, eventArgs.User.Uid)) return false;
|
||||||
|
|
||||||
Owner.Delete();
|
Owner.Delete();
|
||||||
var droppedEnt = Owner.EntityManager.SpawnEntity(_cableDroppedOnCutPrototype, eventArgs.ClickLocation);
|
var droppedEnt = Owner.EntityManager.SpawnEntity(_cableDroppedOnCutPrototype, eventArgs.ClickLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Server.Power.Components
|
|||||||
/// Draws power directly from an MV or HV wire it is on top of.
|
/// Draws power directly from an MV or HV wire it is on top of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class PowerConsumerComponent : BasePowerNetComponent
|
public class PowerConsumerComponent : BaseNetConnectorComponent<IBasePowerNet>
|
||||||
{
|
{
|
||||||
public override string Name => "PowerConsumer";
|
public override string Name => "PowerConsumer";
|
||||||
|
|
||||||
@@ -31,12 +31,12 @@ namespace Content.Server.Power.Components
|
|||||||
|
|
||||||
public PowerState.Load NetworkLoad { get; } = new();
|
public PowerState.Load NetworkLoad { get; } = new();
|
||||||
|
|
||||||
protected override void AddSelfToNet(IPowerNet powerNet)
|
protected override void AddSelfToNet(IBasePowerNet powerNet)
|
||||||
{
|
{
|
||||||
powerNet.AddConsumer(this);
|
powerNet.AddConsumer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void RemoveSelfFromNet(IPowerNet powerNet)
|
protected override void RemoveSelfFromNet(IBasePowerNet powerNet)
|
||||||
{
|
{
|
||||||
powerNet.RemoveConsumer(this);
|
powerNet.RemoveConsumer(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,6 +279,12 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var consumer in net.Consumers)
|
||||||
|
{
|
||||||
|
netNode.Loads.Add(consumer.NetworkLoad.Id);
|
||||||
|
consumer.NetworkLoad.LinkedNetwork = netNode.Id;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var apc in net.Apcs)
|
foreach (var apc in net.Apcs)
|
||||||
{
|
{
|
||||||
var netBattery = apc.Owner.GetComponent<PowerNetworkBatteryComponent>();
|
var netBattery = apc.Owner.GetComponent<PowerNetworkBatteryComponent>();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using Robust.Shared.ViewVariables;
|
|||||||
|
|
||||||
namespace Content.Server.Power.NodeGroups
|
namespace Content.Server.Power.NodeGroups
|
||||||
{
|
{
|
||||||
public interface IApcNet
|
public interface IApcNet : IBasePowerNet
|
||||||
{
|
{
|
||||||
void AddApc(ApcComponent apc);
|
void AddApc(ApcComponent apc);
|
||||||
|
|
||||||
@@ -25,20 +25,18 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
|
|
||||||
void QueueNetworkReconnect();
|
void QueueNetworkReconnect();
|
||||||
|
|
||||||
PowerState.Network NetworkNode { get; }
|
|
||||||
|
|
||||||
GridId? GridId { get; }
|
GridId? GridId { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[NodeGroup(NodeGroupID.Apc)]
|
[NodeGroup(NodeGroupID.Apc)]
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class ApcNet : BaseNetConnectorNodeGroup<BaseApcNetComponent, IApcNet>, IApcNet
|
public class ApcNet : BaseNetConnectorNodeGroup<IApcNet>, IApcNet
|
||||||
{
|
{
|
||||||
private readonly PowerNetSystem _powerNetSystem = EntitySystem.Get<PowerNetSystem>();
|
private readonly PowerNetSystem _powerNetSystem = EntitySystem.Get<PowerNetSystem>();
|
||||||
|
|
||||||
[ViewVariables] public readonly List<ApcComponent> Apcs = new();
|
[ViewVariables] public readonly List<ApcComponent> Apcs = new();
|
||||||
|
|
||||||
[ViewVariables] public readonly List<ApcPowerProviderComponent> Providers = new();
|
[ViewVariables] public readonly List<ApcPowerProviderComponent> Providers = new();
|
||||||
|
[ViewVariables] public readonly List<PowerConsumerComponent> Consumers = new();
|
||||||
|
|
||||||
//Debug property
|
//Debug property
|
||||||
[ViewVariables] private int TotalReceivers => Providers.Sum(provider => provider.LinkedReceivers.Count);
|
[ViewVariables] private int TotalReceivers => Providers.Sum(provider => provider.LinkedReceivers.Count);
|
||||||
@@ -71,7 +69,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
if (apc.Owner.TryGetComponent(out PowerNetworkBatteryComponent? netBattery))
|
if (apc.Owner.TryGetComponent(out PowerNetworkBatteryComponent? netBattery))
|
||||||
netBattery.NetworkBattery.LinkedNetworkDischarging = default;
|
netBattery.NetworkBattery.LinkedNetworkDischarging = default;
|
||||||
|
|
||||||
_powerNetSystem.QueueReconnectApcNet(this);
|
QueueNetworkReconnect();
|
||||||
Apcs.Add(apc);
|
Apcs.Add(apc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +78,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
if (apc.Owner.TryGetComponent(out PowerNetworkBatteryComponent? netBattery))
|
if (apc.Owner.TryGetComponent(out PowerNetworkBatteryComponent? netBattery))
|
||||||
netBattery.NetworkBattery.LinkedNetworkDischarging = default;
|
netBattery.NetworkBattery.LinkedNetworkDischarging = default;
|
||||||
|
|
||||||
_powerNetSystem.QueueReconnectApcNet(this);
|
QueueNetworkReconnect();
|
||||||
Apcs.Remove(apc);
|
Apcs.Remove(apc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +86,28 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
{
|
{
|
||||||
Providers.Add(provider);
|
Providers.Add(provider);
|
||||||
|
|
||||||
_powerNetSystem.QueueReconnectApcNet(this);
|
QueueNetworkReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemovePowerProvider(ApcPowerProviderComponent provider)
|
public void RemovePowerProvider(ApcPowerProviderComponent provider)
|
||||||
{
|
{
|
||||||
Providers.Remove(provider);
|
Providers.Remove(provider);
|
||||||
|
|
||||||
_powerNetSystem.QueueReconnectApcNet(this);
|
QueueNetworkReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddConsumer(PowerConsumerComponent consumer)
|
||||||
|
{
|
||||||
|
consumer.NetworkLoad.LinkedNetwork = default;
|
||||||
|
Consumers.Add(consumer);
|
||||||
|
QueueNetworkReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveConsumer(PowerConsumerComponent consumer)
|
||||||
|
{
|
||||||
|
consumer.NetworkLoad.LinkedNetwork = default;
|
||||||
|
Consumers.Remove(consumer);
|
||||||
|
QueueNetworkReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueNetworkReconnect()
|
public void QueueNetworkReconnect()
|
||||||
@@ -103,7 +115,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
_powerNetSystem.QueueReconnectApcNet(this);
|
_powerNetSystem.QueueReconnectApcNet(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetNetConnectorNet(BaseApcNetComponent netConnectorComponent)
|
protected override void SetNetConnectorNet(IBaseNetConnectorComponent<IApcNet> netConnectorComponent)
|
||||||
{
|
{
|
||||||
netConnectorComponent.Net = this;
|
netConnectorComponent.Net = this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ using Content.Server.Power.Components;
|
|||||||
|
|
||||||
namespace Content.Server.Power.NodeGroups
|
namespace Content.Server.Power.NodeGroups
|
||||||
{
|
{
|
||||||
public abstract class BaseNetConnectorNodeGroup<TNetConnector, TNetType> : BaseNodeGroup
|
public abstract class BaseNetConnectorNodeGroup<TNetType> : BaseNodeGroup
|
||||||
where TNetConnector : BaseNetConnectorComponent<TNetType>
|
|
||||||
{
|
{
|
||||||
public override void LoadNodes(List<Node> groupNodes)
|
public override void LoadNodes(List<Node> groupNodes)
|
||||||
{
|
{
|
||||||
@@ -16,7 +15,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
foreach (var node in groupNodes)
|
foreach (var node in groupNodes)
|
||||||
{
|
{
|
||||||
var newNetConnectorComponents = node.Owner
|
var newNetConnectorComponents = node.Owner
|
||||||
.GetAllComponents<TNetConnector>()
|
.GetAllComponents<IBaseNetConnectorComponent<TNetType>>()
|
||||||
.Where(powerComp => (powerComp.NodeId == null || powerComp.NodeId == node.Name) &&
|
.Where(powerComp => (powerComp.NodeId == null || powerComp.NodeId == node.Name) &&
|
||||||
(NodeGroupID) powerComp.Voltage == node.NodeGroupID)
|
(NodeGroupID) powerComp.Voltage == node.NodeGroupID)
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -28,6 +27,6 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void SetNetConnectorNet(TNetConnector netConnectorComponent);
|
protected abstract void SetNetConnectorNet(IBaseNetConnectorComponent<TNetType> netConnectorComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
Content.Server/Power/NodeGroups/IBasePowerNet.cs
Normal file
14
Content.Server/Power/NodeGroups/IBasePowerNet.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.Pow3r;
|
||||||
|
|
||||||
|
namespace Content.Server.Power.NodeGroups
|
||||||
|
{
|
||||||
|
public interface IBasePowerNet
|
||||||
|
{
|
||||||
|
void AddConsumer(PowerConsumerComponent consumer);
|
||||||
|
|
||||||
|
void RemoveConsumer(PowerConsumerComponent consumer);
|
||||||
|
|
||||||
|
PowerState.Network NetworkNode { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,16 +12,12 @@ using Robust.Shared.ViewVariables;
|
|||||||
|
|
||||||
namespace Content.Server.Power.NodeGroups
|
namespace Content.Server.Power.NodeGroups
|
||||||
{
|
{
|
||||||
public interface IPowerNet
|
public interface IPowerNet : IBasePowerNet
|
||||||
{
|
{
|
||||||
void AddSupplier(PowerSupplierComponent supplier);
|
void AddSupplier(PowerSupplierComponent supplier);
|
||||||
|
|
||||||
void RemoveSupplier(PowerSupplierComponent supplier);
|
void RemoveSupplier(PowerSupplierComponent supplier);
|
||||||
|
|
||||||
void AddConsumer(PowerConsumerComponent consumer);
|
|
||||||
|
|
||||||
void RemoveConsumer(PowerConsumerComponent consumer);
|
|
||||||
|
|
||||||
void AddDischarger(BatteryDischargerComponent discharger);
|
void AddDischarger(BatteryDischargerComponent discharger);
|
||||||
|
|
||||||
void RemoveDischarger(BatteryDischargerComponent discharger);
|
void RemoveDischarger(BatteryDischargerComponent discharger);
|
||||||
@@ -33,7 +29,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
|
|
||||||
[NodeGroup(NodeGroupID.HVPower, NodeGroupID.MVPower)]
|
[NodeGroup(NodeGroupID.HVPower, NodeGroupID.MVPower)]
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class PowerNet : BaseNetConnectorNodeGroup<BasePowerNetComponent, IPowerNet>, IPowerNet
|
public class PowerNet : BaseNetConnectorNodeGroup<IPowerNet>, IPowerNet
|
||||||
{
|
{
|
||||||
private readonly PowerNetSystem _powerNetSystem = EntitySystem.Get<PowerNetSystem>();
|
private readonly PowerNetSystem _powerNetSystem = EntitySystem.Get<PowerNetSystem>();
|
||||||
|
|
||||||
@@ -59,7 +55,7 @@ namespace Content.Server.Power.NodeGroups
|
|||||||
_powerNetSystem.DestroyPowerNet(this);
|
_powerNetSystem.DestroyPowerNet(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetNetConnectorNet(BasePowerNetComponent netConnectorComponent)
|
protected override void SetNetConnectorNet(IBaseNetConnectorComponent<IPowerNet> netConnectorComponent)
|
||||||
{
|
{
|
||||||
netConnectorComponent.Net = this;
|
netConnectorComponent.Net = this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ namespace Content.Server.Power.Pow3r
|
|||||||
battery.LoadingDemandMarked = true;
|
battery.LoadingDemandMarked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network.LastAvailableSupplySum = availableSupplySum;
|
||||||
|
network.LastMaxSupplySum = maxSupplySum;
|
||||||
|
|
||||||
var met = Math.Min(demand, availableSupplySum);
|
var met = Math.Min(demand, availableSupplySum);
|
||||||
|
|
||||||
if (met != 0)
|
if (met != 0)
|
||||||
|
|||||||
@@ -451,6 +451,9 @@ namespace Content.Server.Power.Pow3r
|
|||||||
// "Supplying" means the network is connected to the OUTPUT port of the battery.
|
// "Supplying" means the network is connected to the OUTPUT port of the battery.
|
||||||
[ViewVariables] public List<NodeId> BatteriesDischarging = new();
|
[ViewVariables] public List<NodeId> BatteriesDischarging = new();
|
||||||
|
|
||||||
|
[ViewVariables] public float LastAvailableSupplySum = 0f;
|
||||||
|
[ViewVariables] public float LastMaxSupplySum = 0f;
|
||||||
|
|
||||||
[ViewVariables] [JsonIgnore] public int Height;
|
[ViewVariables] [JsonIgnore] public int Height;
|
||||||
[JsonIgnore] public bool HeightTouched;
|
[JsonIgnore] public bool HeightTouched;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ namespace Content.Server.Weapon.Melee
|
|||||||
var targets = new[] { target };
|
var targets = new[] { target };
|
||||||
SendAnimation(comp.ClickArc, angle, args.User, owner, targets, comp.ClickAttackEffect, false);
|
SendAnimation(comp.ClickArc, angle, args.User, owner, targets, comp.ClickAttackEffect, false);
|
||||||
|
|
||||||
|
RaiseLocalEvent(target.Uid, new AttackedEvent(args.Used, args.User, args.ClickLocation));
|
||||||
|
|
||||||
_damageableSystem.TryChangeDamage(target.Uid,
|
_damageableSystem.TryChangeDamage(target.Uid,
|
||||||
DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
|
DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
|
||||||
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
||||||
@@ -156,6 +158,8 @@ namespace Content.Server.Weapon.Melee
|
|||||||
|
|
||||||
foreach (var entity in hitEntities)
|
foreach (var entity in hitEntities)
|
||||||
{
|
{
|
||||||
|
RaiseLocalEvent(entity.Uid, new AttackedEvent(args.Used, args.User, args.ClickLocation));
|
||||||
|
|
||||||
_damageableSystem.TryChangeDamage(entity.Uid,
|
_damageableSystem.TryChangeDamage(entity.Uid,
|
||||||
DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
|
DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
namespace Content.Shared.Construction
|
namespace Content.Shared.Construction
|
||||||
{
|
{
|
||||||
|
[ImplicitDataDefinitionForInheritors]
|
||||||
public interface IGraphAction
|
public interface IGraphAction
|
||||||
{
|
{
|
||||||
Task PerformAction(IEntity entity, IEntity? user);
|
Task PerformAction(IEntity entity, IEntity? user);
|
||||||
|
|||||||
10
Content.Shared/Electrocution/ElectrocutedComponent.cs
Normal file
10
Content.Shared/Electrocution/ElectrocutedComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Shared.Electrocution
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class ElectrocutedComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Electrocuted";
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Content.Shared/Electrocution/ElectrocutionEvents.cs
Normal file
32
Content.Shared/Electrocution/ElectrocutionEvents.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Shared.Electrocution
|
||||||
|
{
|
||||||
|
public class ElectrocutionAttemptEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly EntityUid TargetUid;
|
||||||
|
public readonly EntityUid? SourceUid;
|
||||||
|
public float SiemensCoefficient = 1f;
|
||||||
|
|
||||||
|
public ElectrocutionAttemptEvent(EntityUid targetUid, EntityUid? sourceUid, float siemensCoefficient)
|
||||||
|
{
|
||||||
|
TargetUid = targetUid;
|
||||||
|
SourceUid = sourceUid;
|
||||||
|
SiemensCoefficient = siemensCoefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ElectrocutedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly EntityUid TargetUid;
|
||||||
|
public readonly EntityUid? SourceUid;
|
||||||
|
public readonly float SiemensCoefficient;
|
||||||
|
|
||||||
|
public ElectrocutedEvent(EntityUid targetUid, EntityUid? sourceUid, float siemensCoefficient)
|
||||||
|
{
|
||||||
|
TargetUid = targetUid;
|
||||||
|
SourceUid = sourceUid;
|
||||||
|
SiemensCoefficient = siemensCoefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Content.Shared/Electrocution/InsulatedComponent.cs
Normal file
35
Content.Shared/Electrocution/InsulatedComponent.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Electrocution
|
||||||
|
{
|
||||||
|
[Friend(typeof(SharedElectrocutionSystem))]
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public class InsulatedComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "Insulated";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Siemens coefficient. Zero means completely insulated.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("coefficient")]
|
||||||
|
public float SiemensCoefficient { get; set; } = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Technically, people could cheat and figure out which budget insulated gloves are gud and which ones are bad.
|
||||||
|
// We might want to rethink this a little bit.
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class InsulatedComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public float SiemensCoefficient { get; private set; }
|
||||||
|
|
||||||
|
public InsulatedComponentState(float siemensCoefficient)
|
||||||
|
{
|
||||||
|
SiemensCoefficient = siemensCoefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
Content.Shared/Electrocution/SharedElectrocutionSystem.cs
Normal file
45
Content.Shared/Electrocution/SharedElectrocutionSystem.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Electrocution
|
||||||
|
{
|
||||||
|
public abstract class SharedElectrocutionSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InsulatedComponent, ElectrocutionAttemptEvent>(OnInsulatedElectrocutionAttempt);
|
||||||
|
SubscribeLocalEvent<InsulatedComponent, ComponentGetState>(OnInsulatedGetState);
|
||||||
|
SubscribeLocalEvent<InsulatedComponent, ComponentHandleState>(OnInsulatedHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInsulatedSiemensCoefficient(EntityUid uid, float siemensCoefficient, InsulatedComponent? insulated = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref insulated))
|
||||||
|
return;
|
||||||
|
|
||||||
|
insulated.SiemensCoefficient = siemensCoefficient;
|
||||||
|
insulated.Dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInsulatedElectrocutionAttempt(EntityUid uid, InsulatedComponent insulated, ElectrocutionAttemptEvent args)
|
||||||
|
{
|
||||||
|
args.SiemensCoefficient *= insulated.SiemensCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInsulatedGetState(EntityUid uid, InsulatedComponent insulated, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new InsulatedComponentState(insulated.SiemensCoefficient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInsulatedHandleState(EntityUid uid, InsulatedComponent insulated, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not InsulatedComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
insulated.SiemensCoefficient = state.SiemensCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,8 +57,10 @@ namespace Content.Shared.Jittering
|
|||||||
/// <param name="frequency">Frequency for jittering. See <see cref="MaxFrequency"/> and <see cref="MinFrequency"/>.</param>
|
/// <param name="frequency">Frequency for jittering. See <see cref="MaxFrequency"/> and <see cref="MinFrequency"/>.</param>
|
||||||
/// <param name="forceValueChange">Whether to change any existing jitter value even if they're greater than the ones we're setting.</param>
|
/// <param name="forceValueChange">Whether to change any existing jitter value even if they're greater than the ones we're setting.</param>
|
||||||
/// <param name="status">The status effects component to modify.</param>
|
/// <param name="status">The status effects component to modify.</param>
|
||||||
|
/// <param name="alerts">The alerts component.</param>
|
||||||
public void DoJitter(EntityUid uid, TimeSpan time, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false,
|
public void DoJitter(EntityUid uid, TimeSpan time, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false,
|
||||||
StatusEffectsComponent? status=null)
|
StatusEffectsComponent? status = null,
|
||||||
|
SharedAlertsComponent? alerts = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref status, false))
|
if (!Resolve(uid, ref status, false))
|
||||||
return;
|
return;
|
||||||
@@ -66,7 +68,7 @@ namespace Content.Shared.Jittering
|
|||||||
amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude);
|
amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude);
|
||||||
frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency);
|
frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency);
|
||||||
|
|
||||||
if (StatusEffects.TryAddStatusEffect<JitteringComponent>(uid, "Jitter", time, status))
|
if (StatusEffects.TryAddStatusEffect<JitteringComponent>(uid, "Jitter", time, status, alerts))
|
||||||
{
|
{
|
||||||
var jittering = EntityManager.GetComponent<JitteringComponent>(uid);
|
var jittering = EntityManager.GetComponent<JitteringComponent>(uid);
|
||||||
|
|
||||||
|
|||||||
@@ -73,4 +73,32 @@ namespace Content.Shared.Weapons.Melee
|
|||||||
ClickLocation = clickLocation;
|
ClickLocation = clickLocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised on entities that have been attacked.
|
||||||
|
/// </summary>
|
||||||
|
public class AttackedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity used to attack, for broadcast purposes.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Used { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that triggered the attack.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity User { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The original location that was clicked by the user.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates ClickLocation { get; }
|
||||||
|
|
||||||
|
public AttackedEvent(IEntity used, IEntity user, EntityCoordinates clickLocation)
|
||||||
|
{
|
||||||
|
Used = used;
|
||||||
|
User = user;
|
||||||
|
ClickLocation = clickLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
electrocuted-component-mob-shocked-by-source-popup-others = { CAPITALIZE(THE($mob)) } is shocked by { THE($source) }!
|
||||||
|
electrocuted-component-mob-shocked-popup-others = { CAPITALIZE(THE($mob)) } is shocked!
|
||||||
|
electrocuted-component-mob-shocked-popup-player = You feel a powerful shock coursing through your body!
|
||||||
@@ -120,3 +120,30 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Hands/Gloves/Color/yellow.rsi
|
sprite: Clothing/Hands/Gloves/Color/yellow.rsi
|
||||||
HeatResistance: 1400
|
HeatResistance: 1400
|
||||||
|
- type: Insulated
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingHandsGlovesColorYellow
|
||||||
|
id: ClothingHandsGlovesColorYellowCheap
|
||||||
|
name: budget insulated gloves
|
||||||
|
description: These gloves are cheap knockoffs of the coveted ones - no way this can end badly.
|
||||||
|
components:
|
||||||
|
- type: Clothing
|
||||||
|
HeatResistance: 0
|
||||||
|
- type: Insulated
|
||||||
|
- type: RandomInsulation
|
||||||
|
# Why repeated numbers? So some numbers are more common, of course!
|
||||||
|
list:
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0
|
||||||
|
- 0.5
|
||||||
|
- 0.5
|
||||||
|
- 0.5
|
||||||
|
- 0.75
|
||||||
|
- 1.25
|
||||||
|
- 1.25
|
||||||
|
- 1.5
|
||||||
|
- 1.5
|
||||||
|
- 1.5
|
||||||
|
- 1.5
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Hands/Gloves/captain.rsi
|
sprite: Clothing/Hands/Gloves/captain.rsi
|
||||||
HeatResistance: 1400
|
HeatResistance: 1400
|
||||||
|
- type: Insulated
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingHandsBase
|
parent: ClothingHandsBase
|
||||||
@@ -126,6 +127,8 @@
|
|||||||
sprite: Clothing/Hands/Gloves/spaceninja.rsi
|
sprite: Clothing/Hands/Gloves/spaceninja.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Hands/Gloves/spaceninja.rsi
|
sprite: Clothing/Hands/Gloves/spaceninja.rsi
|
||||||
|
HeatResistance: 1400
|
||||||
|
- type: Insulated
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingHandsBase
|
parent: ClothingHandsBase
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
- KnockedDown
|
- KnockedDown
|
||||||
- SlowedDown
|
- SlowedDown
|
||||||
- Stutter
|
- Stutter
|
||||||
|
- Electrocution
|
||||||
# Other
|
# Other
|
||||||
- type: Inventory
|
- type: Inventory
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
|
|||||||
@@ -30,6 +30,12 @@
|
|||||||
- !type:DoActsBehavior
|
- !type:DoActsBehavior
|
||||||
acts: ["Destruction"]
|
acts: ["Destruction"]
|
||||||
- type: SubFloorHide
|
- type: SubFloorHide
|
||||||
|
- type: Electrified
|
||||||
|
onBump: false
|
||||||
|
requirePower: true
|
||||||
|
highVoltageNode: power
|
||||||
|
mediumVoltageNode: power
|
||||||
|
lowVoltageNode: power
|
||||||
- type: CableVis
|
- type: CableVis
|
||||||
node: power
|
node: power
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,36 @@
|
|||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Inorganic
|
damageContainer: Inorganic
|
||||||
damageModifierSet: FlimsyMetallic
|
damageModifierSet: FlimsyMetallic
|
||||||
|
- type: PowerConsumer
|
||||||
|
- type: Electrified
|
||||||
|
requirePower: true
|
||||||
|
noWindowInTile: true
|
||||||
|
highVoltageNode: high
|
||||||
|
mediumVoltageNode: medium
|
||||||
|
lowVoltageNode: low
|
||||||
|
- type: NodeContainer
|
||||||
|
nodes:
|
||||||
|
high:
|
||||||
|
!type:CableDeviceNode
|
||||||
|
nodeGroupID: HVPower
|
||||||
|
medium:
|
||||||
|
!type:CableDeviceNode
|
||||||
|
nodeGroupID: MVPower
|
||||||
|
low:
|
||||||
|
!type:CableDeviceNode
|
||||||
|
nodeGroupID: Apc
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Static
|
||||||
|
fixtures:
|
||||||
|
- shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.5,-0.5,0.5,0.5"
|
||||||
|
layer:
|
||||||
|
- Opaque
|
||||||
|
- Impassable
|
||||||
|
- MobImpassable
|
||||||
|
- VaultImpassable
|
||||||
|
- SmallImpassable
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
@@ -36,7 +66,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: GrilleBroken
|
id: GrilleBroken
|
||||||
parent: Grille
|
parent: BaseStructure
|
||||||
name: grille
|
name: grille
|
||||||
description: A flimsy framework of iron rods. It has seen better days.
|
description: A flimsy framework of iron rods. It has seen better days.
|
||||||
components:
|
components:
|
||||||
@@ -59,12 +89,15 @@
|
|||||||
fixtures:
|
fixtures:
|
||||||
- shape:
|
- shape:
|
||||||
!type:PhysShapeAabb
|
!type:PhysShapeAabb
|
||||||
bounds: "-0.45,-0.45,0.45,0.45"
|
bounds: "-0.5,-0.5,0.5,0.5"
|
||||||
mass: 50
|
mass: 50
|
||||||
layer:
|
layer:
|
||||||
- Passable
|
- Passable
|
||||||
mask:
|
mask:
|
||||||
- Passable
|
- Passable
|
||||||
|
- type: Damageable
|
||||||
|
damageContainer: Inorganic
|
||||||
|
damageModifierSet: FlimsyMetallic
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
|
|||||||
44
Resources/Prototypes/Entities/Virtual/electrocution.yml
Normal file
44
Resources/Prototypes/Entities/Virtual/electrocution.yml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Special entity used to attach to power networks as load when somebody gets electrocuted.
|
||||||
|
- type: entity
|
||||||
|
id: VirtualElectrocutionLoadHVPower
|
||||||
|
name: ELECTROCUTION ENTITY YOU SHOULD NOT SEE THIS
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: NodeContainer
|
||||||
|
nodes:
|
||||||
|
electrocution: !type:ElectrocutionNode
|
||||||
|
nodeGroupID: HVPower
|
||||||
|
|
||||||
|
- type: PowerConsumer
|
||||||
|
voltage: High
|
||||||
|
drawRate: 50000
|
||||||
|
- type: Electrocution
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: VirtualElectrocutionLoadMVPower
|
||||||
|
name: ELECTROCUTION ENTITY YOU SHOULD NOT SEE THIS
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: NodeContainer
|
||||||
|
nodes:
|
||||||
|
electrocution: !type:ElectrocutionNode
|
||||||
|
nodeGroupID: MVPower
|
||||||
|
|
||||||
|
- type: PowerConsumer
|
||||||
|
voltage: Medium
|
||||||
|
drawRate: 50000
|
||||||
|
- type: Electrocution
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: VirtualElectrocutionLoadApc
|
||||||
|
name: ELECTROCUTION ENTITY YOU SHOULD NOT SEE THIS
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: NodeContainer
|
||||||
|
nodes:
|
||||||
|
electrocution: !type:ElectrocutionNode
|
||||||
|
nodeGroupID: Apc
|
||||||
|
- type: PowerConsumer
|
||||||
|
voltage: Apc
|
||||||
|
drawRate: 50000
|
||||||
|
- type: Electrocution
|
||||||
@@ -18,3 +18,6 @@
|
|||||||
|
|
||||||
- type: statusEffect
|
- type: statusEffect
|
||||||
id: Stutter
|
id: Stutter
|
||||||
|
|
||||||
|
- type: statusEffect
|
||||||
|
id: Electrocution
|
||||||
|
|||||||
Reference in New Issue
Block a user