using System.Collections.Generic;
-using System.Diagnostics;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Destructible;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Maps;
using Robust.Shared;
using System.Linq;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Client.GameObjects;
using System.Linq;
using System.Numerics;
-using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Client.UserInterface.Controls;
-using Content.Shared.Alert;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.IdentityManagement;
-using Content.Shared.Inventory;
using Content.Shared.MedicalScanner;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Nutrition.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.ResourceManagement;
-using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using System.Numerics;
using Content.Client.StatusIcon;
using Content.Client.UserInterface.Systems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Atmos.Rotting;
-using Content.Shared.Damage;
using Content.Shared.Inventory.Events;
using Content.Shared.Mobs.Components;
using Content.Shared.Overlays;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
using System.Linq;
+using Content.Shared.Damage.Components;
namespace Content.Client.Overlays;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Administration.Systems;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using System.Linq;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Execution;
using Content.Shared.FixedPoint;
using Content.Shared.Ghost;
await server.WaitAssertion(() =>
{
// Heal all damage first (possible low pressure damage taken)
- damageableSystem.SetAllDamage(player, damageableComp, 0);
+ damageableSystem.ClearAllDamage((player, damageableComp));
consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
var lethalDamageThreshold = mobThresholdsComp.Thresholds.Keys.Last();
await server.WaitAssertion(() =>
{
// Heal all damage first (possible low pressure damage taken)
- damageableSystem.SetAllDamage(player, damageableComp, 0);
+ damageableSystem.ClearAllDamage((player, damageableComp));
consoleHost.GetSessionShell(playerMan.Sessions.First()).ExecuteCommand("suicide");
var lethalDamageThreshold = mobThresholdsComp.Thresholds.Keys.Last();
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
var damageType = Server.ProtoMan.Index(BluntDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(10));
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
- await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));
+ await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target).Value, damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(comp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero));
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
});
- // Test SetAll function
- sDamageableSystem.SetAllDamage(sDamageableEntity, sDamageableComponent, 10);
+ // Test SetAll and ClearAll function
+ sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 10);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(10 * sDamageableComponent.Damage.DamageDict.Count)));
- sDamageableSystem.SetAllDamage(sDamageableEntity, sDamageableComponent, 0);
+ sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 0);
+ Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
+ sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 10);
+ Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(10 * sDamageableComponent.Damage.DamageDict.Count)));
+ sDamageableSystem.ClearAllDamage((sDamageableEntity, sDamageableComponent));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Test 'wasted' healing
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible.Thresholds.Triggers;
using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
sTestThresholdListenerSystem.ThresholdsReached.Clear();
// Heal both classes of damage to 0
- sDamageableSystem.SetAllDamage(sDestructibleEntity, sDamageableComponent, 0);
+ sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// No new thresholds reached, healing should not trigger it
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
threshold.TriggersOnce = true;
// Heal brute and burn back to 0
- sDamageableSystem.SetAllDamage(sDestructibleEntity, sDamageableComponent, 0);
+ sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// No new thresholds reached from healing
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible.Thresholds.Triggers;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible.Thresholds;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Content.Server.Destructible.Thresholds;
using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio.Systems;
using Content.Shared.Destructible;
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
// Set damage to 0
- sDamageableSystem.SetAllDamage(sDestructibleEntity, sDamageableComponent, 0);
+ sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// Damage for 100, up to 100
sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 10, true);
sTestThresholdListenerSystem.ThresholdsReached.Clear();
// Heal all damage
- sDamageableSystem.SetAllDamage(sDestructibleEntity, sDamageableComponent, 0);
+ sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// Damage up to 50
sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true);
sTestThresholdListenerSystem.ThresholdsReached.Clear();
// Heal the entity completely
- sDamageableSystem.SetAllDamage(sDestructibleEntity, sDamageableComponent, 0);
+ sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// Check that the entity has 0 damage
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Shared.CCVar;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
var damageable = entMan.GetComponent<DamageableComponent>(entity);
var prototype = protoMan.Index(BluntDamageType);
- damageableSystem.SetDamage(entity, damageable, new DamageSpecifier(prototype, FixedPoint2.New(401)));
+ damageableSystem.SetDamage((entity, damageable), new DamageSpecifier(prototype, FixedPoint2.New(401)));
Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId));
});
using Content.IntegrationTests.Tests.Movement;
using Content.Server.NPC.HTN;
-using Content.Shared.Damage;
-using Content.Shared.FixedPoint;
+using Content.Shared.Damage.Components;
using Content.Shared.Item.ItemToggle;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Mobs;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Content.IntegrationTests.Tests.Interaction;
using Content.Server.VendingMachines;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.VendingMachines;
using Robust.Shared.Prototypes;
// Damage the vending machine to the point that it breaks
var damageType = ProtoMan.Index(TestDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(100));
- await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));
+ await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target).Value, damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(damageableComp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero), $"{VendingMachineProtoId} did not take damage.");
}
using Content.Shared.Cargo.Prototypes;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Prototypes;
using Content.Shared.Storage.Components;
using Content.Shared.VendingMachines;
restock = entityManager.SpawnEntity("TestRestockExplode", coordinates);
var damageSpec = new DamageSpecifier(prototypeManager.Index(TestDamageType), 100);
- var damageResult = damageableSystem.TryChangeDamage(restock, damageSpec);
+ var damageResult = damageableSystem.ChangeDamage(restock, damageSpec);
#pragma warning disable NUnit2045
- Assert.That(damageResult, Is.Not.Null,
- "Received null damageResult when attempting to damage restock box.");
+ Assert.That(!damageResult.Empty, "Received empty damageResult when attempting to damage restock box.");
- Assert.That((int) damageResult!.GetTotal(), Is.GreaterThan(0),
- "Box damage result was not greater than 0.");
+ Assert.That((int) damageResult.GetTotal(), Is.GreaterThan(0), "Box damage result was not greater than 0.");
#pragma warning restore NUnit2045
});
await server.WaitRunTicks(15);
using Content.IntegrationTests.Tests.Interaction;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
using Content.Shared.Construction;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Roles;
using Content.Shared.StationRecords;
using Content.Server.Administration.Components;
using Content.Server.Atmos.EntitySystems;
-using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Electrocution;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Clothing.Components;
using Content.Shared.Clumsy;
using Content.Shared.Cluwne;
-using Content.Shared.Damage;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Electrocution;
using Robust.Shared.Utility;
using System.Numerics;
using System.Threading;
+using Content.Shared.Damage.Components;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.Administration.Systems;
var maxPressureCapped = Math.Min(maxPressure, ent.Comp.MaxEffectivePressure);
var appliedDamage = ScaleDamage(ent, ent.Comp.BaseDamage, maxPressureCapped);
- _damage.TryChangeDamage(ent, appliedDamage, ignoreResistances: true, interruptsDoAfters: false);
+ _damage.ChangeDamage(ent.Owner, appliedDamage, ignoreResistances: true, interruptsDoAfters: false);
SetIsTakingDamageState(ent, true);
}
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using System.Linq;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Robust.Shared.Threading;
namespace Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Components;
using Content.Shared.Alert;
using Content.Shared.Atmos;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.IgnitionSource;
using Content.Shared.Interaction;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Body.Events;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Temperature.Components;
using Robust.Server.Containers;
using Robust.Shared.Physics.Components;
using Content.Shared.Bed.Components;
using Content.Shared.Bed.Sleep;
using Content.Shared.Buckle.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs.Systems;
namespace Content.Server.Bed
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
using Content.Shared.Bible;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Ghost.Roles.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Timing;
using Content.Shared.Verbs;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Random;
}
}
- var damage = _damageableSystem.TryChangeDamage(args.Target.Value, component.Damage, true, origin: uid);
-
- if (damage == null || damage.Empty)
+ if (_damageableSystem.TryChangeDamage(args.Target.Value, component.Damage, true, origin: uid))
{
var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-none-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
_popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.Medium);
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.EntityConditions;
using Content.Shared.EntityConditions.Conditions.Body;
if (ent.Comp.SuffocationCycles == 2)
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
- _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false);
+ _damageableSys.ChangeDamage(ent.Owner, ent.Comp.Damage, interruptsDoAfters: false);
if (ent.Comp.SuffocationCycles < ent.Comp.SuffocationCycleThreshold)
return;
if (ent.Comp.SuffocationCycles >= 2)
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating");
- _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery);
+ _damageableSys.ChangeDamage(ent.Owner, ent.Comp.DamageRecovery);
var ev = new StopSuffocatingEvent();
RaiseLocalEvent(ent, ref ev);
-using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Access.Components;
using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Interaction;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
using Content.Shared.Storage.Components;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
-using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.CardboardBox;
//Relay damage to the mover
private void OnDamage(EntityUid uid, CardboardBoxComponent component, DamageChangedEvent args)
{
- if (args.DamageDelta != null && args.DamageIncreased)
- {
- _damageable.TryChangeDamage(component.Mover, args.DamageDelta, origin: args.Origin);
- }
+ if (args.DamageDelta == null || !args.DamageIncreased || component.Mover is not { } mover)
+ return;
+
+ _damageable.ChangeDamage(mover, args.DamageDelta, origin: args.Origin);
}
private void OnEntInserted(EntityUid uid, CardboardBoxComponent component, EntInsertedIntoContainerMessage args)
using Content.Server.Hands.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Chat;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Database;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
+using Content.Shared.Damage.Systems;
+
namespace Content.Server.Chat.Systems;
using Content.Shared.Chat;
using Content.Shared.Chemistry.Components;
using Content.Shared.Cloning;
using Content.Shared.Chat;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Clumsy;
using Content.Shared.Cluwne;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Popups;
using Content.Server.Destructible;
using Content.Shared.Construction;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Content.Server.Construction.Conditions;
using Content.Shared.Administration;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
-using Content.Shared.FixedPoint;
+using Content.Shared.Damage.Systems;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.ForceSay;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Server.Damage.Components;
-using Content.Shared.Damage;
-using Robust.Shared.Player;
using Content.Shared.Weapons.Melee.Events;
using System.Linq;
+using Content.Shared.Damage.Systems;
namespace Content.Server.Damage.Systems;
/// </summary>
public sealed class DamageOnLandSystem : EntitySystem
{
- [Dependency] private readonly DamageableSystem _damageableSystem = default!;
+ [Dependency] private readonly Shared.Damage.Systems.DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
{
public sealed class DamageOnToolInteractSystem : EntitySystem
{
- [Dependency] private readonly DamageableSystem _damageableSystem = default!;
+ [Dependency] private readonly Shared.Damage.Systems.DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!;
&& itemToggle.Activated
&& !welder.TankSafe)
{
- var dmg = _damageableSystem.TryChangeDamage(args.Target, weldingDamage, origin: args.User);
-
- if (dmg != null)
+ if (_damageableSystem.TryChangeDamage(args.Target, weldingDamage, out var dmg, origin: args.User))
+ {
_adminLogger.Add(LogType.Damaged,
$"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a welder to deal {dmg.GetTotal():damage} damage to {ToPrettyString(args.Target):target}");
+ }
args.Handled = true;
}
else if (component.DefaultDamage is {} damage
&& _toolSystem.HasQuality(args.Used, component.Tools))
{
- var dmg = _damageableSystem.TryChangeDamage(args.Target, damage, origin: args.User);
-
- if (dmg != null)
+ if (_damageableSystem.TryChangeDamage(args.Target, damage, out var dmg, origin: args.User))
+ {
_adminLogger.Add(LogType.Damaged,
$"{ToPrettyString(args.User):user} used {ToPrettyString(args.Used):used} as a tool to deal {dmg.GetTotal():damage} damage to {ToPrettyString(args.Target):target}");
+ }
args.Handled = true;
}
{
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly GunSystem _guns = default!;
- [Dependency] private readonly DamageableSystem _damageable = default!;
+ [Dependency] private readonly Shared.Damage.Systems.DamageableSystem _damageable = default!;
[Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
if (TerminatingOrDeleted(args.Target))
return;
- var dmg = _damageable.TryChangeDamage(args.Target, component.Damage * _damageable.UniversalThrownDamageModifier, component.IgnoreResistances, origin: args.Component.Thrower);
+ var dmg = _damageable.ChangeDamage(args.Target, component.Damage * _damageable.UniversalThrownDamageModifier, component.IgnoreResistances, origin: args.Component.Thrower);
// Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying.
- if (dmg != null && HasComp<MobStateComponent>(args.Target))
+ if (HasComp<MobStateComponent>(args.Target))
_adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {dmg.GetTotal():damage} damage from collision");
- if (dmg is { Empty: false })
+ if (!dmg.Empty)
{
_color.RaiseEffect(Color.Red, [args.Target], Filter.Pvs(args.Target, entityManager: EntityManager));
}
using Content.Server.Damage.Components;
using Content.Server.Popups;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Content.Server.Damage.Components;
using Content.Server.Destructible;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.Rounding;
using Robust.Shared.Prototypes;
/// <summary>
/// Tests all triggers in a DestructibleComponent to see how expensive it is to query them.
/// </summary>
- public void TestAllTriggers(List<Entity<DamageableComponent, DestructibleComponent>> destructibles)
+ public void TestAllTriggers(List<Entity<Shared.Damage.Components.DamageableComponent, DestructibleComponent>> destructibles)
{
foreach (var (uid, damageable, destructible) in destructibles)
{
/// <summary>
/// Tests all behaviours in a DestructibleComponent to see how expensive it is to query them.
/// </summary>
- public void TestAllBehaviors(List<Entity<DamageableComponent, DestructibleComponent>> destructibles)
+ public void TestAllBehaviors(List<Entity<Shared.Damage.Components.DamageableComponent, DestructibleComponent>> destructibles)
{
foreach (var (uid, damageable, destructible) in destructibles)
{
using Content.Server.Stack;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Destructible;
using Content.Shared.Destructible.Thresholds.Triggers;
/// <summary>
/// Check if the given threshold should trigger.
/// </summary>
- public bool Triggered(DamageThreshold threshold, Entity<DamageableComponent> owner)
+ public bool Triggered(DamageThreshold threshold, Entity<Shared.Damage.Components.DamageableComponent> owner)
{
if (threshold.Trigger == null)
return false;
/// <summary>
/// Check if the conditions for the given threshold are currently true.
/// </summary>
- public bool Reached(DamageThreshold threshold, Entity<DamageableComponent> owner)
+ public bool Reached(DamageThreshold threshold, Entity<Shared.Damage.Components.DamageableComponent> owner)
{
if (threshold.Trigger == null)
return false;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Disposal.Tube;
using Content.Shared.Body.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Disposal.Components;
using Content.Shared.Item;
using Content.Shared.Throwing;
using Content.Server.NPC;
using Content.Server.NPC.Systems;
using Content.Server.Pinpointer;
-using Content.Shared.Damage;
using Content.Shared.Dragon;
using Content.Shared.Examine;
using Content.Shared.Sprite;
-using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Serialization.Manager;
using System.Numerics;
-using Robust.Shared.Audio;
+using Content.Shared.Damage.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Utility;
using Content.Server.Administration.Logs;
-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.Weapons.Melee;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Electrocution;
using Content.Shared.IdentityManagement;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
-using Robust.Shared.Map;
using Robust.Shared.Physics.Events;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
? _stun.TryUpdateParalyzeDuration(uid, time * ParalyzeTimeMultiplier)
: _stun.TryAddParalyzeDuration(uid, time * ParalyzeTimeMultiplier);
}
-
+
// TODO: Sparks here.
if (shockDamage is { } dmg)
{
- var actual = _damageable.TryChangeDamage(uid,
- new DamageSpecifier(_prototypeManager.Index(DamageType), dmg), origin: sourceUid);
-
- if (actual != null)
+ if (_damageable.TryChangeDamage(uid, new DamageSpecifier(_prototypeManager.Index(DamageType), dmg), out var damage, origin: sourceUid))
{
_adminLogger.Add(LogType.Electrocution,
- $"{ToPrettyString(uid):entity} received {actual.GetTotal():damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : ""):source}");
+ $"{ToPrettyString(uid):entity} received {damage:damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : ""):source}");
}
}
using Content.Server.Atmos.Components;
-using Content.Server.Destructible;
using Content.Shared.Atmos;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Explosion;
-using Content.Shared.Explosion.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Map.Components;
-using System.Linq;
using System.Numerics;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Explosion.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Database;
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
-using Content.Shared.Explosion.EntitySystems;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Projectiles;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
-using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
float? fireStacksOnIgnite,
EntityUid? cause)
{
- if (originalDamage != null)
+ if (originalDamage is not null)
{
GetEntitiesToDamage(uid, originalDamage, id);
foreach (var (entity, damage) in _toDamage)
{
+ if (!_damageableQuery.TryComp(entity, out var damageable))
+ continue;
+
+ // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
+ _damageableSystem.TryChangeDamage((entity, damageable), damage, ignoreResistances: true, ignoreGlobalModifiers: true);
+
if (_actorQuery.HasComp(entity))
{
- // Log damage to player entities only, cause this will create a massive amount of log spam otherwise.
- if (cause != null)
- {
+ // Log damage to player entities only; this will create a massive amount of log spam otherwise.
+ if (cause is not null)
_adminLogger.Add(LogType.ExplosionHit, LogImpact.Medium, $"Explosion of {ToPrettyString(cause):actor} dealt {damage.GetTotal()} damage to {ToPrettyString(entity):subject}");
- }
else
- {
_adminLogger.Add(LogType.ExplosionHit, LogImpact.Medium, $"Explosion at {epicenter:epicenter} dealt {damage.GetTotal()} damage to {ToPrettyString(entity):subject}");
- }
-
}
-
- // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
- _damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true, ignoreGlobalModifiers: true);
-
}
}
private readonly IEntityManager _entMan;
private readonly ExplosionSystem _system;
private readonly SharedMapSystem _mapSystem;
- private readonly DamageableSystem _damageable;
+ private readonly Shared.Damage.Systems.DamageableSystem _damageable;
public readonly EntityUid VisualEnt;
EntityUid visualEnt,
EntityUid? cause,
SharedMapSystem mapSystem,
- DamageableSystem damageable)
+ Shared.Damage.Systems.DamageableSystem damageable)
{
VisualEnt = visualEnt;
Cause = cause;
using Content.Shared.Atmos.Components;
using Content.Shared.Camera;
using Content.Shared.CCVar;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
-using Content.Server.Ghost.Components;
using Content.Server.Mind;
using Content.Server.Roles.Jobs;
using Content.Shared.Actions;
using Content.Shared.CCVar;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.Eye;
DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
- _damageable.TryChangeDamage(playerEntity, damage, true);
+ _damageable.ChangeDamage(playerEntity.Value, damage, true);
}
}
using Content.Server.Body.Systems;
using Content.Server.Popups;
using Content.Shared.Actions;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Guardian;
if (args.DamageDelta == null || component.Host == null || component.DamageShare == 0)
return;
- _damageSystem.TryChangeDamage(
- component.Host,
+ _damageSystem.ChangeDamage(
+ component.Host.Value,
args.DamageDelta * component.DamageShare,
origin: args.Origin,
ignoreResistances: true,
using Content.Server.Body.Systems;
using Content.Server.Destructible;
-using Content.Server.Examine;
using Content.Server.Polymorph.Components;
using Content.Server.Popups;
using Content.Shared.Body.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Content.Server.NPC.HTN;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Server.Kitchen.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
-using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Stacks;
using Content.Server.Construction.Components;
using Content.Shared.Chat;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Temperature.Components;
-using Robust.Shared.Utility;
namespace Content.Server.Kitchen.EntitySystems
{
using Content.Server.Lightning;
using Content.Server.Lightning.Components;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Robust.Server.GameObjects;
namespace Content.Server.Tesla.EntitySystems;
{
DamageSpecifier damage = new();
damage.DamageDict.Add("Structural", uid.Comp.DamageFromLightning);
- _damageable.TryChangeDamage(uid, damage, true);
+ _damageable.ChangeDamage(uid.Owner, damage, true);
if (uid.Comp.LightningExplode)
{
using Content.Server.Mech.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.ActionBlocker;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
component.PilotSlot.ContainedEntity != null)
{
var damage = args.DamageDelta * component.MechToPilotDamageMultiplier;
- _damageable.TryChangeDamage(component.PilotSlot.ContainedEntity, damage);
+ _damageable.ChangeDamage(component.PilotSlot.ContainedEntity.Value, damage);
}
}
using Content.Server.PowerCell;
using Content.Shared.Traits.Assorted;
using Content.Shared.Chat;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
using Content.Shared.Item.ItemToggle;
using Content.Server.PowerCell;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Server.Administration.Logs;
using Content.Server.Destructible;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Systems;
using Content.Shared.NPC.Components;
using Content.Server.NPC.Pathfinding;
using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Silicons.Bots;
using Content.Server.NPC.Components;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Server.NPC.Queries.Queries;
using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Fluids.Components;
using Content.Shared.Inventory;
using Robust.Shared.Utility;
using Content.Shared.Atmos.Components;
using System.Linq;
+using Content.Shared.Damage.Components;
using Content.Shared.Temperature.Components;
namespace Content.Server.NPC.Systems;
using Content.Server.Ninja.Events;
using Content.Server.Power.EntitySystems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Interaction;
using Content.Shared.Ninja.Components;
using Content.Shared.Ninja.Systems;
using Content.Shared.Timing;
using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
-using Robust.Shared.Timing;
-using Robust.Shared.Prototypes;
namespace Content.Server.Ninja.Systems;
_audio.PlayPvs(comp.Sound, target);
- _damageable.TryChangeDamage(target, comp.StunDamage, false, true, null, origin: uid);
+ _damageable.ChangeDamage(target, comp.StunDamage, origin: uid);
_stun.TryAddParalyzeDuration(target, comp.StunTime);
// short cooldown to prevent instant stunlocking
using Content.Server.Popups;
using Content.Shared.Body.Components;
using Content.Shared.Atmos;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Emag.Systems;
using Content.Shared.IdentityManagement;
using Content.Server.Polymorph.Components;
using Content.Shared.Buckle;
using Content.Shared.Coordinates;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
_mobThreshold.GetScaledDamage(uid, child, out var damage) &&
damage != null)
{
- _damageable.SetDamage(child, damageParent, damage);
+ _damageable.SetDamage((child, damageParent), damage);
}
if (configuration.Inventory == PolymorphInventoryChange.Transfer)
_mobThreshold.GetScaledDamage(uid, parent, out var damage) &&
damage != null)
{
- _damageable.SetDamage(parent, damageParent, damage);
+ _damageable.SetDamage((parent, damageParent), damage);
}
if (component.Configuration.Inventory == PolymorphInventoryChange.Transfer)
using Content.Server.Effects;
using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Camera;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Projectiles;
damageRequired -= damageableComponent.TotalDamage;
damageRequired = FixedPoint2.Max(damageRequired, FixedPoint2.Zero);
}
- var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, damageable: damageableComponent, origin: component.Shooter);
var deleted = Deleted(target);
- if (modifiedDamage is not null && Exists(component.Shooter))
+ if (_damageableSystem.TryChangeDamage((target, damageableComponent), ev.Damage, out var damage, component.IgnoreResistances, origin: component.Shooter) && Exists(component.Shooter))
{
- if (modifiedDamage.AnyPositive() && !deleted)
+ if (!deleted)
{
_color.RaiseEffect(Color.Red, new List<EntityUid> { target }, Filter.Pvs(target, entityManager: EntityManager));
}
_adminLogger.Add(LogType.BulletHit,
LogImpact.Medium,
- $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {modifiedDamage.GetTotal():damage} damage");
- }
+ $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {damage:damage} damage");
- // If penetration is to be considered, we need to do some checks to see if the projectile should stop.
- if (modifiedDamage is not null && component.PenetrationThreshold != 0)
- {
- // If a damage type is required, stop the bullet if the hit entity doesn't have that type.
- if (component.PenetrationDamageTypeRequirement != null)
+ // If penetration is to be considered, we need to do some checks to see if the projectile should stop.
+ if (component.PenetrationThreshold != 0)
{
- var stopPenetration = false;
- foreach (var requiredDamageType in component.PenetrationDamageTypeRequirement)
+ // If a damage type is required, stop the bullet if the hit entity doesn't have that type.
+ if (component.PenetrationDamageTypeRequirement != null)
{
- if (!modifiedDamage.DamageDict.Keys.Contains(requiredDamageType))
+ var stopPenetration = false;
+ foreach (var requiredDamageType in component.PenetrationDamageTypeRequirement)
{
- stopPenetration = true;
- break;
+ if (!damage.DamageDict.Keys.Contains(requiredDamageType))
+ {
+ stopPenetration = true;
+ break;
+ }
}
+ if (stopPenetration)
+ component.ProjectileSpent = true;
}
- if (stopPenetration)
- component.ProjectileSpent = true;
- }
-
- // If the object won't be destroyed, it "tanks" the penetration hit.
- if (modifiedDamage.GetTotal() < damageRequired)
- {
- component.ProjectileSpent = true;
- }
- if (!component.ProjectileSpent)
- {
- component.PenetrationAmount += damageRequired;
- // The projectile has dealt enough damage to be spent.
- if (component.PenetrationAmount >= component.PenetrationThreshold)
+ // If the object won't be destroyed, it "tanks" the penetration hit.
+ if (damage.GetTotal() < damageRequired)
{
component.ProjectileSpent = true;
}
+
+ if (!component.ProjectileSpent)
+ {
+ component.PenetrationAmount += damageRequired;
+ // The projectile has dealt enough damage to be spent.
+ if (component.PenetrationAmount >= component.PenetrationThreshold)
+ {
+ component.ProjectileSpent = true;
+ }
+ }
+ }
+ else
+ {
+ component.ProjectileSpent = true;
}
- }
- else
- {
- component.ProjectileSpent = true;
}
if (!deleted)
{
- _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound);
+ _guns.PlayImpactSound(target, damage, component.SoundHit, component.ForceSound);
if (!args.OurBody.LinearVelocity.IsLengthZero())
_sharedCameraRecoil.KickCamera(target, args.OurBody.LinearVelocity.Normalized());
return;
DamageSpecifier dspec = new();
dspec.DamageDict.Add("Cold", damage.Value);
- _damage.TryChangeDamage(args.Args.Target, dspec, true, origin: uid);
+ _damage.ChangeDamage(args.Args.Target.Value, dspec, true, origin: uid);
args.Handled = true;
}
using Content.Server.GameTicking;
using Content.Server.Store.Systems;
using Content.Shared.Alert;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Eye;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using System.Numerics;
+using Content.Shared.Damage.Components;
namespace Content.Server.Shuttles.Systems;
damageSpec.DamageDict["Blunt"] = scaledDamage;
damageSpec.DamageDict["Structural"] = scaledDamage * _structuralDamage;
- _damageSys.TryChangeDamage(localEnt, damageSpec, damageable: damageable);
+ _damageSys.ChangeDamage((localEnt, damageable), damageSpec);
}
// might've been destroyed
if (TerminatingOrDeleted(localEnt) || EntityManager.IsQueuedForDeletion(localEnt))
using Content.Server.Station.Systems;
using Content.Server.Stunnable;
using Content.Shared.Buckle.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Light.Components;
using Content.Shared.Movement.Events;
using Content.Shared.Salvage;
using System.Numerics;
using Content.Server.Audio;
-using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Shuttles.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Shuttles.Components;
using Content.Shared.Temperature;
-using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.DeviceNetwork;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Shared.Alert;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DoAfter;
_battery.SetCharge(ent, battery.MaxCharge);
}
- if (TryComp<DamageableComponent>(ent, out var damageable))
- {
- _damageable.SetAllDamage(ent, damageable, 0);
- }
+ _damageable.ClearAllDamage(ent.Owner);
}
protected override void OnAiInsert(Entity<StationAiCoreComponent> ent, ref EntInsertedIntoContainerMessage args)
using Content.Server.Destructible;
using Content.Server.PowerCell;
using Content.Shared.Speech.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Speech;
using Robust.Shared.Random;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Spreader;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Content.Server.Temperature.Components;
using Content.Shared.Alert;
using Content.Shared.Atmos;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Inventory;
using Content.Shared.Rejuvenate;
using Content.Server.Vocalization.Systems;
using Content.Shared.Cargo;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible;
using Content.Shared.Emp;
using Content.Shared.Power;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Weapons.Ranged.Components;
using Robust.Shared.Map;
using Content.Server.StationEvents.Components;
using Content.Server.Speech.Components;
using Content.Shared.Body.Components;
-using Content.Shared.Chat;
using Content.Shared.CombatMode;
using Content.Shared.CombatMode.Pacification;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
tempComp.ColdDamage.ClampMax(0);
//Heals the zombie from all the damage it took while human
- if (TryComp<DamageableComponent>(target, out var damageablecomp))
- _damageable.SetAllDamage(target, damageablecomp, 0);
+ _damageable.ClearAllDamage(target);
_mobState.ChangeMobState(target, MobState.Alive);
_faction.ClearFactions(target, dirty: false);
-using System.Linq;
using Content.Shared.NPC.Prototypes;
using Content.Server.Actions;
using Content.Server.Body.Systems;
using Content.Shared.Bed.Sleep;
using Content.Shared.Cloning.Events;
using Content.Shared.Chat;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Humanoid;
using Content.Shared.Inventory;
using Content.Shared.Mind;
var curTime = _timing.CurTime;
// Hurt the living infected
- var query = EntityQueryEnumerator<PendingZombieComponent, DamageableComponent, MobStateComponent>();
+ var query = EntityQueryEnumerator<PendingZombieComponent, Shared.Damage.Components.DamageableComponent, MobStateComponent>();
while (query.MoveNext(out var uid, out var comp, out var damage, out var mobState))
{
// Process only once per second
? comp.CritDamageMultiplier
: 1f;
- _damageable.TryChangeDamage(uid, comp.Damage * multiplier, true, false, damage);
+ _damageable.ChangeDamage((uid, damage), comp.Damage * multiplier, true, false);
}
// Heal the zombified
- var zombQuery = EntityQueryEnumerator<ZombieComponent, DamageableComponent, MobStateComponent>();
+ var zombQuery = EntityQueryEnumerator<ZombieComponent, Shared.Damage.Components.DamageableComponent, MobStateComponent>();
while (zombQuery.MoveNext(out var uid, out var comp, out var damage, out var mobState))
{
// Process only once per second
: 1f;
// Gradual healing for living zombies.
- _damageable.TryChangeDamage(uid, comp.PassiveHealing * multiplier, true, false, damage);
+ _damageable.ChangeDamage((uid, damage), comp.PassiveHealing * multiplier, true, false);
}
}
using Content.Shared.Clothing.Components;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Inventory;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.ForceSay;
+using Content.Shared.Damage.Systems;
using Content.Shared.Emoting;
using Content.Shared.Examine;
using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Damage;
-using Content.Shared.Damage.Prototypes;
-using Robust.Shared.Audio;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
private void OnUserDamageModified(EntityUid uid, BlockingUserComponent component, DamageModifyEvent args)
{
- if (TryComp<BlockingComponent>(component.BlockingItem, out var blocking))
- {
- if (args.Damage.GetTotal() <= 0)
- return;
+ if (component.BlockingItem is not { } item || !TryComp<BlockingComponent>(item, out var blocking))
+ return;
+
+ if (args.Damage.GetTotal() <= 0)
+ return;
- // A shield should only block damage it can itself absorb. To determine that we need the Damageable component on it.
- if (!TryComp<DamageableComponent>(component.BlockingItem, out var dmgComp))
- return;
+ // A shield should only block damage it can itself absorb. To determine that we need the Damageable component on it.
+ if (!TryComp<DamageableComponent>(item, out var dmgComp))
+ return;
- var blockFraction = blocking.IsBlocking ? blocking.ActiveBlockFraction : blocking.PassiveBlockFraction;
- blockFraction = Math.Clamp(blockFraction, 0, 1);
- _damageable.TryChangeDamage(component.BlockingItem, blockFraction * args.OriginalDamage);
+ var blockFraction = blocking.IsBlocking ? blocking.ActiveBlockFraction : blocking.PassiveBlockFraction;
+ blockFraction = Math.Clamp(blockFraction, 0, 1);
+ _damageable.TryChangeDamage((item, dmgComp), blockFraction * args.OriginalDamage);
- var modify = new DamageModifierSet();
- foreach (var key in dmgComp.Damage.DamageDict.Keys)
- {
- modify.Coefficients.TryAdd(key, 1 - blockFraction);
- }
+ var modify = new DamageModifierSet();
+ foreach (var key in dmgComp.Damage.DamageDict.Keys)
+ {
+ modify.Coefficients.TryAdd(key, 1 - blockFraction);
+ }
- args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modify);
+ args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modify);
- if (blocking.IsBlocking && !args.Damage.Equals(args.OriginalDamage))
- {
- _audio.PlayPvs(blocking.BlockSound, uid);
- }
+ if (blocking.IsBlocking && !args.Damage.Equals(args.OriginalDamage))
+ {
+ _audio.PlayPvs(blocking.BlockSound, uid);
}
}
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.EntityEffects.Effects.Solution;
-using Content.Shared.EntityEffects.Effects.Transform;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids;
using Content.Shared.Forensics.Components;
// bloodloss damage is based on the base value, and modified by how low your blood level is.
var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage);
- _damageableSystem.TryChangeDamage(uid, amt,
- ignoreResistances: false, interruptsDoAfters: false);
+ _damageableSystem.TryChangeDamage(uid, amt, ignoreResistances: false, interruptsDoAfters: false);
// Apply dizziness as a symptom of bloodloss.
// The effect is applied in a way that it will never be cleared without being healthy.
{
// TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly
var damage = new DamageSpecifier(Prototypes.Index(BloodlossDamageType), 300);
- Damageable.TryChangeDamage(bodyEnt, damage);
+ Damageable.ChangeDamage(bodyEnt.Owner, damage);
}
}
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Movement.Systems;
using Content.Shared.Standing;
using Robust.Shared.Containers;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Body.Components;
using Content.Shared.Changeling.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Humanoid;
if (damage.Damage.DamageDict.TryGetValue(damagePoints.Key, out var val) && val > comp.DevourConsumeDamageCap)
return;
}
- _damageable.TryChangeDamage(target, comp.DamagePerTick, true, true, damage, user);
+ _damageable.ChangeDamage((target.Value, damage), comp.DamagePerTick, true, true, user);
}
/// <summary>
+using System.Linq;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
-using System.Linq;
namespace Content.Shared.Chat;
appliedDamageSpecifier.DamageDict[key] = Math.Ceiling((double) (value * lethalAmountOfDamage / totalDamage));
}
- _damageableSystem.TryChangeDamage(target, appliedDamageSpecifier, true, origin: target);
+ _damageableSystem.ChangeDamage(target.AsNullable(), appliedDamageSpecifier, true, origin: target);
}
/// <summary>
}
var damage = new DamageSpecifier(damagePrototype, lethalAmountOfDamage);
- _damageableSystem.TryChangeDamage(target, damage, true, origin: target);
+ _damageableSystem.ChangeDamage(target.AsNullable(), damage, true, origin: target);
}
}
using Content.Shared.Buckle.Components;
using Content.Shared.Climbing.Components;
using Content.Shared.Climbing.Events;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.DragDrop;
using Content.Shared.Hands.Components;
using Content.Shared.Clothing.Components;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Inventory;
using Content.Shared.Movement.Systems;
using Content.Shared.Chemistry.Hypospray.Events;
using Content.Shared.Climbing.Components;
using Content.Shared.Climbing.Events;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.IdentityManagement;
using Content.Shared.Medical;
using Content.Shared.Popups;
args.Cancelled = true; // fail to catch
if (ent.Comp.CatchingFailDamage != null)
- _damageable.TryChangeDamage(ent, ent.Comp.CatchingFailDamage, origin: args.Item);
+ _damageable.ChangeDamage(ent.Owner, ent.Comp.CatchingFailDamage, origin: args.Item);
// Collisions don't work properly with PopupPredicted or PlayPredicted.
// So we make this server only.
return;
if (ent.Comp.GunShootFailDamage != null)
- _damageable.TryChangeDamage(ent, ent.Comp.GunShootFailDamage, origin: ent);
+ _damageable.ChangeDamage(ent.Owner, ent.Comp.GunShootFailDamage, origin: ent);
_stun.TryUpdateParalyzeDuration(ent, ent.Comp.GunShootFailStunTime);
{
stunTime = bonkComp.BonkTime;
if (bonkComp.BonkDamage != null)
- _damageable.TryChangeDamage(target, bonkComp.BonkDamage, true);
+ _damageable.ChangeDamage(target.Owner, bonkComp.BonkDamage, true);
}
_stun.TryUpdateParalyzeDuration(target, stunTime);
+using Content.Shared.Damage.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.StatusIcon;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
-namespace Content.Shared.Damage
+namespace Content.Shared.Damage.Components;
+
+/// <summary>
+/// Component that allows entities to take damage.
+/// </summary>
+/// <remarks>
+/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. DamageContainers
+/// may also have resistances to certain damage types, defined via a <see cref="DamageModifierSetPrototype"/>.
+/// </remarks>
+[RegisterComponent]
+[NetworkedComponent]
+[Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)]
+public sealed partial class DamageableComponent : Component
{
/// <summary>
- /// Component that allows entities to take damage.
+ /// This <see cref="DamageContainerPrototype"/> specifies what damage types are supported by this component.
+ /// If null, all damage types will be supported.
+ /// </summary>
+ [DataField("damageContainer")]
+ // ReSharper disable once InconsistentNaming - This is wrong but fixing it is potentially annoying for downstreams.
+ public ProtoId<DamageContainerPrototype>? DamageContainerID;
+
+ /// <summary>
+ /// This <see cref="DamageModifierSetPrototype"/> will be applied to any damage that is dealt to this container,
+ /// unless the damage explicitly ignores resistances.
/// </summary>
/// <remarks>
- /// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. DamageContainers
- /// may also have resistances to certain damage types, defined via a <see cref="DamageModifierSetPrototype"/>.
+ /// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here
+ /// to reduce duplication.
/// </remarks>
- [RegisterComponent]
- [NetworkedComponent]
- [Access(typeof(DamageableSystem), Other = AccessPermissions.ReadExecute)]
- public sealed partial class DamageableComponent : Component
- {
- /// <summary>
- /// This <see cref="DamageContainerPrototype"/> specifies what damage types are supported by this component.
- /// If null, all damage types will be supported.
- /// </summary>
- [DataField("damageContainer")]
- public ProtoId<DamageContainerPrototype>? DamageContainerID;
-
- /// <summary>
- /// This <see cref="DamageModifierSetPrototype"/> will be applied to any damage that is dealt to this container,
- /// unless the damage explicitly ignores resistances.
- /// </summary>
- /// <remarks>
- /// Though DamageModifierSets can be deserialized directly, we only want to use the prototype version here
- /// to reduce duplication.
- /// </remarks>
- [DataField("damageModifierSet")]
- public ProtoId<DamageModifierSetPrototype>? DamageModifierSetId;
+ [DataField("damageModifierSet")]
+ public ProtoId<DamageModifierSetPrototype>? DamageModifierSetId;
- /// <summary>
- /// All the damage information is stored in this <see cref="DamageSpecifier"/>.
- /// </summary>
- /// <remarks>
- /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
- /// </remarks>
- [DataField(readOnly: true)] // TODO FULL GAME SAVE
- public DamageSpecifier Damage = new();
-
- /// <summary>
- /// Damage, indexed by <see cref="DamageGroupPrototype"/> ID keys.
- /// </summary>
- /// <remarks>
- /// Groups which have no members that are supported by this component will not be present in this
- /// dictionary.
- /// </remarks>
- [ViewVariables] public Dictionary<string, FixedPoint2> DamagePerGroup = new();
+ /// <summary>
+ /// All the damage information is stored in this <see cref="DamageSpecifier"/>.
+ /// </summary>
+ /// <remarks>
+ /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
+ /// </remarks>
+ [DataField(readOnly: true)] //TODO FULL GAME SAVE
+ public DamageSpecifier Damage = new();
- /// <summary>
- /// The sum of all damages in the DamageableComponent.
- /// </summary>
- [ViewVariables]
- public FixedPoint2 TotalDamage;
+ /// <summary>
+ /// Damage, indexed by <see cref="DamageGroupPrototype"/> ID keys.
+ /// </summary>
+ /// <remarks>
+ /// Groups which have no members that are supported by this component will not be present in this
+ /// dictionary.
+ /// </remarks>
+ [ViewVariables] public Dictionary<string, FixedPoint2> DamagePerGroup = new();
- [DataField("radiationDamageTypes")]
- public List<ProtoId<DamageTypePrototype>> RadiationDamageTypeIDs = new() { "Radiation" };
+ /// <summary>
+ /// The sum of all damages in the DamageableComponent.
+ /// </summary>
+ [ViewVariables]
+ public FixedPoint2 TotalDamage;
- /// <summary>
- /// Group types that affect the pain overlay.
- /// </summary>
- /// TODO: Add support for adding damage types specifically rather than whole damage groups
- [DataField]
- public List<ProtoId<DamageGroupPrototype>> PainDamageGroups = new() { "Brute", "Burn" };
+ [DataField("radiationDamageTypes")]
+ // ReSharper disable once UseCollectionExpression - Cannot refactor this as it's a potential sandbox violation.
+ public List<ProtoId<DamageTypePrototype>> RadiationDamageTypeIDs = new() { "Radiation" };
- [DataField]
- public Dictionary<MobState, ProtoId<HealthIconPrototype>> HealthIcons = new()
- {
- { MobState.Alive, "HealthIconFine" },
- { MobState.Critical, "HealthIconCritical" },
- { MobState.Dead, "HealthIconDead" },
- };
+ /// <summary>
+ /// Group types that affect the pain overlay.
+ /// </summary>
+ /// TODO: Add support for adding damage types specifically rather than whole damage groups
+ [DataField]
+ // ReSharper disable once UseCollectionExpression - Cannot refactor this as it's a potential sandbox volation.
+ public List<ProtoId<DamageGroupPrototype>> PainDamageGroups = new() { "Brute", "Burn" };
- [DataField]
- public ProtoId<HealthIconPrototype> RottingIcon = "HealthIconRotting";
+ [DataField]
+ public Dictionary<MobState, ProtoId<HealthIconPrototype>> HealthIcons = new()
+ {
+ { MobState.Alive, "HealthIconFine" },
+ { MobState.Critical, "HealthIconCritical" },
+ { MobState.Dead, "HealthIconDead" },
+ };
- [DataField]
- public FixedPoint2? HealthBarThreshold;
- }
+ [DataField]
+ public ProtoId<HealthIconPrototype> RottingIcon = "HealthIconRotting";
- [Serializable, NetSerializable]
- public sealed class DamageableComponentState : ComponentState
- {
- public readonly Dictionary<string, FixedPoint2> DamageDict;
- public readonly string? DamageContainerId;
- public readonly string? ModifierSetId;
- public readonly FixedPoint2? HealthBarThreshold;
+ [DataField]
+ public FixedPoint2? HealthBarThreshold;
+}
- public DamageableComponentState(
- Dictionary<string, FixedPoint2> damageDict,
- string? damageContainerId,
- string? modifierSetId,
- FixedPoint2? healthBarThreshold)
- {
- DamageDict = damageDict;
- DamageContainerId = damageContainerId;
- ModifierSetId = modifierSetId;
- HealthBarThreshold = healthBarThreshold;
- }
- }
+[Serializable, NetSerializable]
+public sealed class DamageableComponentState(
+ Dictionary<string, FixedPoint2> damageDict,
+ ProtoId<DamageContainerPrototype>? damageContainerId,
+ ProtoId<DamageModifierSetPrototype>? modifierSetId,
+ FixedPoint2? healthBarThreshold)
+ : ComponentState
+{
+ public readonly Dictionary<string, FixedPoint2> DamageDict = damageDict;
+ public readonly ProtoId<DamageContainerPrototype>? DamageContainerId = damageContainerId;
+ public readonly ProtoId<DamageModifierSetPrototype>? ModifierSetId = modifierSetId;
+ public readonly FixedPoint2? HealthBarThreshold = healthBarThreshold;
}
+using Content.Shared.Damage.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared.Damage.Components;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
using System.Linq;
using System.Text.Json.Serialization;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
+using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization;
namespace Content.Shared.Damage.Prototypes
{
+using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization;
namespace Content.Shared.Damage.Prototypes
{
}
}
- totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, entity.Comp.IgnoreResistances, origin: entity);
+ totalDamage = _damageableSystem.ChangeDamage(args.User, totalDamage, entity.Comp.IgnoreResistances, origin: entity);
- if (totalDamage != null && totalDamage.AnyPositive())
+ if (totalDamage.AnyPositive())
{
_adminLogger.Add(LogType.Damaged, $"{ToPrettyString(args.User):user} injured themselves by attacking {ToPrettyString(entity):target} and received {totalDamage.GetTotal():damage} damage");
_audioSystem.PlayPredicted(entity.Comp.InteractSound, entity, args.User);
// or checking the entity for the comp itself if the inventory didn't work
if (protectiveEntity.Comp == null && TryComp<DamageOnInteractProtectionComponent>(args.User, out var protectiveComp))
protectiveEntity = (args.User, protectiveComp);
-
+
// if protectiveComp isn't null after all that, it means the user has protection,
// so let's calculate how much they resist
}
}
- totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, origin: args.Target);
+ totalDamage = _damageableSystem.ChangeDamage(args.User, totalDamage, origin: args.Target);
- if (totalDamage != null && totalDamage.AnyPositive())
+ if (totalDamage.AnyPositive())
{
// Record this interaction and determine when a user is allowed to interact with this entity again
entity.Comp.LastInteraction = _gameTiming.CurTime;
--- /dev/null
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Prototypes;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Damage.Systems;
+
+public sealed partial class DamageableSystem
+{
+ /// <summary>
+ /// Directly sets the damage specifier of a damageable component.
+ /// </summary>
+ /// <remarks>
+ /// Useful for some unfriendly folk. Also ensures that cached values are updated and that a damage changed
+ /// event is raised.
+ /// </remarks>
+ public void SetDamage(Entity<DamageableComponent?> ent, DamageSpecifier damage)
+ {
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
+ return;
+
+ ent.Comp.Damage = damage;
+
+ OnEntityDamageChanged((ent, ent.Comp));
+ }
+
+ /// <summary>
+ /// Applies damage specified via a <see cref="DamageSpecifier"/>.
+ /// </summary>
+ /// <remarks>
+ /// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
+ /// function just applies the container's resistances (unless otherwise specified) and then changes the
+ /// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
+ /// </remarks>
+ /// <returns>
+ /// If the attempt was successful or not.
+ /// </returns>
+ public bool TryChangeDamage(
+ Entity<DamageableComponent?> ent,
+ DamageSpecifier damage,
+ bool ignoreResistances = false,
+ bool interruptsDoAfters = true,
+ EntityUid? origin = null,
+ bool ignoreGlobalModifiers = false
+ )
+ {
+ //! Empty just checks if the DamageSpecifier is _literally_ empty, as in, is internal dictionary of damage types is empty.
+ // If you deal 0.0 of some damage type, Empty will be false!
+ return !TryChangeDamage(ent, damage, out _, ignoreResistances, interruptsDoAfters, origin, ignoreGlobalModifiers);
+ }
+
+ /// <summary>
+ /// Applies damage specified via a <see cref="DamageSpecifier"/>.
+ /// </summary>
+ /// <remarks>
+ /// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
+ /// function just applies the container's resistances (unless otherwise specified) and then changes the
+ /// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
+ /// </remarks>
+ /// <returns>
+ /// If the attempt was successful or not.
+ /// </returns>
+ public bool TryChangeDamage(
+ Entity<DamageableComponent?> ent,
+ DamageSpecifier damage,
+ out DamageSpecifier newDamage,
+ bool ignoreResistances = false,
+ bool interruptsDoAfters = true,
+ EntityUid? origin = null,
+ bool ignoreGlobalModifiers = false
+ )
+ {
+ //! Empty just checks if the DamageSpecifier is _literally_ empty, as in, is internal dictionary of damage types is empty.
+ // If you deal 0.0 of some damage type, Empty will be false!
+ newDamage = ChangeDamage(ent, damage, ignoreResistances, interruptsDoAfters, origin, ignoreGlobalModifiers);
+ return !damage.Empty;
+ }
+
+ /// <summary>
+ /// Applies damage specified via a <see cref="DamageSpecifier"/>.
+ /// </summary>
+ /// <remarks>
+ /// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
+ /// function just applies the container's resistances (unless otherwise specified) and then changes the
+ /// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
+ /// </remarks>
+ /// <returns>
+ /// The actual amount of damage taken, as a DamageSpecifier.
+ /// </returns>
+ public DamageSpecifier ChangeDamage(
+ Entity<DamageableComponent?> ent,
+ DamageSpecifier damage,
+ bool ignoreResistances = false,
+ bool interruptsDoAfters = true,
+ EntityUid? origin = null,
+ bool ignoreGlobalModifiers = false
+ )
+ {
+ var damageDone = new DamageSpecifier();
+
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
+ return damageDone;
+
+ if (damage.Empty)
+ return damageDone;
+
+ var before = new BeforeDamageChangedEvent(damage, origin);
+ RaiseLocalEvent(ent, ref before);
+
+ if (before.Cancelled)
+ return damageDone;
+
+ // Apply resistances
+ if (!ignoreResistances)
+ {
+ if (
+ ent.Comp.DamageModifierSetId != null &&
+ _prototypeManager.Resolve(ent.Comp.DamageModifierSetId, out var modifierSet)
+ )
+ damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
+
+ // TODO DAMAGE
+ // byref struct event.
+ var ev = new DamageModifyEvent(damage, origin);
+ RaiseLocalEvent(ent, ev);
+ damage = ev.Damage;
+
+ if (damage.Empty)
+ return damageDone;
+ }
+
+ if (!ignoreGlobalModifiers)
+ damage = ApplyUniversalAllModifiers(damage);
+
+
+ damageDone.DamageDict.EnsureCapacity(damage.DamageDict.Count);
+
+ var dict = ent.Comp.Damage.DamageDict;
+ foreach (var (type, value) in damage.DamageDict)
+ {
+ // CollectionsMarshal my beloved.
+ if (!dict.TryGetValue(type, out var oldValue))
+ continue;
+
+ var newValue = FixedPoint2.Max(FixedPoint2.Zero, oldValue + value);
+ if (newValue == oldValue)
+ continue;
+
+ dict[type] = newValue;
+ damageDone.DamageDict[type] = newValue - oldValue;
+ }
+
+ if (!damageDone.Empty)
+ OnEntityDamageChanged((ent, ent.Comp), damageDone, interruptsDoAfters, origin);
+
+ return damageDone;
+ }
+
+ /// <summary>
+ /// Applies the two universal "All" modifiers, if set.
+ /// Individual damage source modifiers are set in their respective code.
+ /// </summary>
+ /// <param name="damage">The damage to be changed.</param>
+ public DamageSpecifier ApplyUniversalAllModifiers(DamageSpecifier damage)
+ {
+ // Checks for changes first since they're unlikely in normal play.
+ if (
+ MathHelper.CloseToPercent(UniversalAllDamageModifier, 1f) &&
+ MathHelper.CloseToPercent(UniversalAllHealModifier, 1f)
+ )
+ return damage;
+
+ foreach (var (key, value) in damage.DamageDict)
+ {
+ if (value == 0)
+ continue;
+
+ if (value > 0)
+ {
+ damage.DamageDict[key] *= UniversalAllDamageModifier;
+
+ continue;
+ }
+
+ if (value < 0)
+ damage.DamageDict[key] *= UniversalAllHealModifier;
+ }
+
+ return damage;
+ }
+
+ public void ClearAllDamage(Entity<DamageableComponent?> ent)
+ {
+ SetAllDamage(ent, FixedPoint2.Zero);
+ }
+
+ /// <summary>
+ /// Sets all damage types supported by a <see cref="Components.DamageableComponent"/> to the specified value.
+ /// </summary>
+ /// <remarks>
+ /// Does nothing If the given damage value is negative.
+ /// </remarks>
+ public void SetAllDamage(Entity<DamageableComponent?> ent, FixedPoint2 newValue)
+ {
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
+ return;
+
+ if (newValue < 0)
+ return;
+
+ foreach (var type in ent.Comp.Damage.DamageDict.Keys)
+ {
+ ent.Comp.Damage.DamageDict[type] = newValue;
+ }
+
+ // Setting damage does not count as 'dealing' damage, even if it is set to a larger value, so we pass an
+ // empty damage delta.
+ OnEntityDamageChanged((ent, ent.Comp), new DamageSpecifier());
+ }
+
+ /// <summary>
+ /// Set's the damage modifier set prototype for this entity.
+ /// </summary>
+ /// <param name="ent">The entity we're setting the modifier set of.</param>
+ /// <param name="damageModifierSetId">The prototype we're setting.</param>
+ public void SetDamageModifierSetId(Entity<DamageableComponent?> ent, ProtoId<DamageModifierSetPrototype>? damageModifierSetId)
+ {
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
+ return;
+
+ ent.Comp.DamageModifierSetId = damageModifierSetId;
+
+ Dirty(ent);
+ }
+}
-namespace Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+
+namespace Content.Shared.Damage.Systems;
public sealed partial class DamageableSystem
{
{
foreach (var (uid, damageable) in damageables)
{
- TryChangeDamage(uid, damage, damageable: damageable);
+ TryChangeDamage((uid, damageable), damage);
}
}
}
--- /dev/null
+using Content.Shared.CCVar;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Prototypes;
+using Content.Shared.FixedPoint;
+using Content.Shared.Inventory;
+using Content.Shared.Radiation.Events;
+using Content.Shared.Rejuvenate;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Damage.Systems;
+
+public sealed partial class DamageableSystem
+{
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<DamageableComponent, ComponentInit>(DamageableInit);
+ SubscribeLocalEvent<DamageableComponent, ComponentHandleState>(DamageableHandleState);
+ SubscribeLocalEvent<DamageableComponent, ComponentGetState>(DamageableGetState);
+ SubscribeLocalEvent<DamageableComponent, OnIrradiatedEvent>(OnIrradiated);
+ SubscribeLocalEvent<DamageableComponent, RejuvenateEvent>(OnRejuvenate);
+
+ _appearanceQuery = GetEntityQuery<AppearanceComponent>();
+ _damageableQuery = GetEntityQuery<DamageableComponent>();
+
+ // Damage modifier CVars are updated and stored here to be queried in other systems.
+ // Note that certain modifiers requires reloading the guidebook.
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestAllDamageModifier,
+ value =>
+ {
+ UniversalAllDamageModifier = value;
+ _chemistryGuideData.ReloadAllReagentPrototypes();
+ _explosion.ReloadMap();
+ },
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestAllHealModifier,
+ value =>
+ {
+ UniversalAllHealModifier = value;
+ _chemistryGuideData.ReloadAllReagentPrototypes();
+ },
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestProjectileDamageModifier,
+ value => UniversalProjectileDamageModifier = value,
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestMeleeDamageModifier,
+ value => UniversalMeleeDamageModifier = value,
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestProjectileDamageModifier,
+ value => UniversalProjectileDamageModifier = value,
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestHitscanDamageModifier,
+ value => UniversalHitscanDamageModifier = value,
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestReagentDamageModifier,
+ value =>
+ {
+ UniversalReagentDamageModifier = value;
+ _chemistryGuideData.ReloadAllReagentPrototypes();
+ },
+ true
+ );
+ Subs.CVar(
+ _config,
+ CCVars.PlaytestReagentHealModifier,
+ value =>
+ {
+ UniversalReagentHealModifier = value;
+ _chemistryGuideData.ReloadAllReagentPrototypes();
+ },
+ 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
+ );
+ }
+
+ /// <summary>
+ /// Initialize a damageable component
+ /// </summary>
+ private void DamageableInit(Entity<DamageableComponent> ent, ref ComponentInit _)
+ {
+ if (
+ ent.Comp.DamageContainerID is null ||
+ !_prototypeManager.Resolve(ent.Comp.DamageContainerID, out var damageContainerPrototype)
+ )
+ {
+ // No DamageContainerPrototype was given. So we will allow the container to support all damage types
+ foreach (var type in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
+ {
+ ent.Comp.Damage.DamageDict.TryAdd(type.ID, FixedPoint2.Zero);
+ }
+ }
+ else
+ {
+ // Initialize damage dictionary, using the types and groups from the damage
+ // container prototype
+ foreach (var type in damageContainerPrototype.SupportedTypes)
+ {
+ ent.Comp.Damage.DamageDict.TryAdd(type, FixedPoint2.Zero);
+ }
+
+ foreach (var groupId in damageContainerPrototype.SupportedGroups)
+ {
+ var group = _prototypeManager.Index(groupId);
+ foreach (var type in group.DamageTypes)
+ {
+ ent.Comp.Damage.DamageDict.TryAdd(type, FixedPoint2.Zero);
+ }
+ }
+ }
+
+ ent.Comp.Damage.GetDamagePerGroup(_prototypeManager, ent.Comp.DamagePerGroup);
+ ent.Comp.TotalDamage = ent.Comp.Damage.GetTotal();
+ }
+
+ private void OnIrradiated(Entity<DamageableComponent> ent, ref OnIrradiatedEvent args)
+ {
+ var damageValue = FixedPoint2.New(args.TotalRads);
+
+ // Radiation should really just be a damage group instead of a list of types.
+ DamageSpecifier damage = new();
+ foreach (var typeId in ent.Comp.RadiationDamageTypeIDs)
+ {
+ damage.DamageDict.Add(typeId, damageValue);
+ }
+
+ ChangeDamage(ent.Owner, damage, interruptsDoAfters: false, origin: args.Origin);
+ }
+
+ private void OnRejuvenate(Entity<DamageableComponent> ent, ref RejuvenateEvent args)
+ {
+ // Do this so that the state changes when we set the damage
+ _mobThreshold.SetAllowRevives(ent, true);
+ ClearAllDamage(ent.AsNullable());
+ _mobThreshold.SetAllowRevives(ent, false);
+ }
+
+ private void DamageableHandleState(Entity<DamageableComponent> ent, ref ComponentHandleState args)
+ {
+ if (args.Current is not DamageableComponentState state)
+ return;
+
+ ent.Comp.DamageContainerID = state.DamageContainerId;
+ ent.Comp.DamageModifierSetId = state.ModifierSetId;
+ ent.Comp.HealthBarThreshold = state.HealthBarThreshold;
+
+ // Has the damage actually changed?
+ DamageSpecifier newDamage = new() { DamageDict = new Dictionary<string, FixedPoint2>(state.DamageDict) };
+ var delta = newDamage - ent.Comp.Damage;
+ delta.TrimZeros();
+
+ if (delta.Empty)
+ return;
+
+ ent.Comp.Damage = newDamage;
+
+ OnEntityDamageChanged(ent, delta);
+ }
+}
+
+/// <summary>
+/// Raised before damage is done, so stuff can cancel it if necessary.
+/// </summary>
+[ByRefEvent]
+public record struct BeforeDamageChangedEvent(DamageSpecifier Damage, EntityUid? Origin = null, bool Cancelled = false);
+
+/// <summary>
+/// Raised on an entity when damage is about to be dealt,
+/// in case anything else needs to modify it other than the base
+/// damageable component.
+///
+/// For example, armor.
+/// </summary>
+public sealed class DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null)
+ : EntityEventArgs, IInventoryRelayEvent
+{
+ // Whenever locational damage is a thing, this should just check only that bit of armour.
+ public SlotFlags TargetSlots => ~SlotFlags.POCKET;
+
+ public readonly DamageSpecifier OriginalDamage = damage;
+ public DamageSpecifier Damage = damage;
+}
+
+public sealed class DamageChangedEvent : EntityEventArgs
+{
+ /// <summary>
+ /// This is the component whose damage was changed.
+ /// </summary>
+ /// <remarks>
+ /// Given that nearly every component that cares about a change in the damage, needs to know the
+ /// current damage values, directly passing this information prevents a lot of duplicate
+ /// Owner.TryGetComponent() calls.
+ /// </remarks>
+ public readonly DamageableComponent Damageable;
+
+ /// <summary>
+ /// The amount by which the damage has changed. If the damage was set directly to some number, this will be
+ /// null.
+ /// </summary>
+ public readonly DamageSpecifier? DamageDelta;
+
+ /// <summary>
+ /// Was any of the damage change dealing damage, or was it all healing?
+ /// </summary>
+ public readonly bool DamageIncreased;
+
+ /// <summary>
+ /// Does this event interrupt DoAfters?
+ /// Note: As provided in the constructor, this *does not* account for DamageIncreased.
+ /// As written into the event, this *does* account for DamageIncreased.
+ /// </summary>
+ public readonly bool InterruptsDoAfters;
+
+ /// <summary>
+ /// Contains the entity which caused the change in damage, if any was responsible.
+ /// </summary>
+ public readonly EntityUid? Origin;
+
+ public DamageChangedEvent(
+ DamageableComponent damageable,
+ DamageSpecifier? damageDelta,
+ bool interruptsDoAfters,
+ EntityUid? origin
+ )
+ {
+ Damageable = damageable;
+ DamageDelta = damageDelta;
+ Origin = origin;
+
+ if (DamageDelta is null)
+ return;
+
+ foreach (var damageChange in DamageDelta.DamageDict.Values)
+ {
+ if (damageChange <= 0)
+ continue;
+
+ DamageIncreased = true;
+
+ break;
+ }
+
+ InterruptsDoAfters = interruptsDoAfters && DamageIncreased;
+ }
+}
using System.Linq;
-using Content.Shared.CCVar;
using Content.Shared.Chemistry;
-using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Components;
using Content.Shared.Explosion.EntitySystems;
-using Content.Shared.FixedPoint;
-using Content.Shared.Inventory;
-using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
-using Content.Shared.Radiation.Events;
-using Content.Shared.Rejuvenate;
using Robust.Shared.Configuration;
using Robust.Shared.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
-namespace Content.Shared.Damage
-{
- public sealed partial class DamageableSystem : EntitySystem
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly INetManager _netMan = default!;
- [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;
-
- public float UniversalAllDamageModifier { get; private set; } = 1f;
- public float UniversalAllHealModifier { get; private set; } = 1f;
- public float UniversalMeleeDamageModifier { get; private set; } = 1f;
- public float UniversalProjectileDamageModifier { get; private set; } = 1f;
- public float UniversalHitscanDamageModifier { get; private set; } = 1f;
- public float UniversalReagentDamageModifier { get; private set; } = 1f;
- public float UniversalReagentHealModifier { get; private set; } = 1f;
- public float UniversalExplosionDamageModifier { get; private set; } = 1f;
- public float UniversalThrownDamageModifier { get; private set; } = 1f;
- public float UniversalTopicalsHealModifier { get; private set; } = 1f;
- public float UniversalMobDamageModifier { get; private set; } = 1f;
-
- public override void Initialize()
- {
- SubscribeLocalEvent<DamageableComponent, ComponentInit>(DamageableInit);
- SubscribeLocalEvent<DamageableComponent, ComponentHandleState>(DamageableHandleState);
- SubscribeLocalEvent<DamageableComponent, ComponentGetState>(DamageableGetState);
- SubscribeLocalEvent<DamageableComponent, OnIrradiatedEvent>(OnIrradiated);
- SubscribeLocalEvent<DamageableComponent, RejuvenateEvent>(OnRejuvenate);
-
- _appearanceQuery = GetEntityQuery<AppearanceComponent>();
- _damageableQuery = GetEntityQuery<DamageableComponent>();
-
- // Damage modifier CVars are updated and stored here to be queried in other systems.
- // Note that certain modifiers requires reloading the guidebook.
- Subs.CVar(_config, CCVars.PlaytestAllDamageModifier, value =>
- {
- UniversalAllDamageModifier = value;
- _chemistryGuideData.ReloadAllReagentPrototypes();
- _explosion.ReloadMap();
- }, true);
- Subs.CVar(_config, CCVars.PlaytestAllHealModifier, value =>
- {
- UniversalAllHealModifier = value;
- _chemistryGuideData.ReloadAllReagentPrototypes();
- }, true);
- Subs.CVar(_config, CCVars.PlaytestProjectileDamageModifier, value => UniversalProjectileDamageModifier = value, true);
- Subs.CVar(_config, CCVars.PlaytestMeleeDamageModifier, value => UniversalMeleeDamageModifier = value, true);
- Subs.CVar(_config, CCVars.PlaytestProjectileDamageModifier, value => UniversalProjectileDamageModifier = value, true);
- Subs.CVar(_config, CCVars.PlaytestHitscanDamageModifier, value => UniversalHitscanDamageModifier = value, true);
- Subs.CVar(_config, CCVars.PlaytestReagentDamageModifier, value =>
- {
- UniversalReagentDamageModifier = value;
- _chemistryGuideData.ReloadAllReagentPrototypes();
- }, true);
- Subs.CVar(_config, CCVars.PlaytestReagentHealModifier, value =>
- {
- UniversalReagentHealModifier = value;
- _chemistryGuideData.ReloadAllReagentPrototypes();
- }, 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);
- }
-
- /// <summary>
- /// Initialize a damageable component
- /// </summary>
- private void DamageableInit(EntityUid uid, DamageableComponent component, ComponentInit _)
- {
- if (component.DamageContainerID != null &&
- _prototypeManager.Resolve<DamageContainerPrototype>(component.DamageContainerID,
- out var damageContainerPrototype))
- {
- // Initialize damage dictionary, using the types and groups from the damage
- // container prototype
- foreach (var type in damageContainerPrototype.SupportedTypes)
- {
- component.Damage.DamageDict.TryAdd(type, FixedPoint2.Zero);
- }
-
- foreach (var groupId in damageContainerPrototype.SupportedGroups)
- {
- var group = _prototypeManager.Index<DamageGroupPrototype>(groupId);
- foreach (var type in group.DamageTypes)
- {
- component.Damage.DamageDict.TryAdd(type, FixedPoint2.Zero);
- }
- }
- }
- else
- {
- // No DamageContainerPrototype was given. So we will allow the container to support all damage types
- foreach (var type in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
- {
- component.Damage.DamageDict.TryAdd(type.ID, FixedPoint2.Zero);
- }
- }
-
- component.Damage.GetDamagePerGroup(_prototypeManager, component.DamagePerGroup);
- component.TotalDamage = component.Damage.GetTotal();
- }
-
- /// <summary>
- /// Directly sets the damage specifier of a damageable component.
- /// </summary>
- /// <remarks>
- /// Useful for some unfriendly folk. Also ensures that cached values are updated and that a damage changed
- /// event is raised.
- /// </remarks>
- public void SetDamage(EntityUid uid, DamageableComponent damageable, DamageSpecifier damage)
- {
- damageable.Damage = damage;
- DamageChanged(uid, damageable);
- }
-
- /// <summary>
- /// If the damage in a DamageableComponent was changed, this function should be called.
- /// </summary>
- /// <remarks>
- /// This updates cached damage information, flags the component as dirty, and raises a damage changed event.
- /// The damage changed event is used by other systems, such as damage thresholds.
- /// </remarks>
- public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSpecifier? damageDelta = null,
- bool interruptsDoAfters = true, EntityUid? origin = null)
- {
- component.Damage.GetDamagePerGroup(_prototypeManager, component.DamagePerGroup);
- component.TotalDamage = component.Damage.GetTotal();
- Dirty(uid, component);
-
- if (_appearanceQuery.TryGetComponent(uid, out var appearance) && damageDelta != null)
- {
- 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));
- }
-
- /// <summary>
- /// Applies damage specified via a <see cref="DamageSpecifier"/>.
- /// </summary>
- /// <remarks>
- /// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
- /// function just applies the container's resistances (unless otherwise specified) and then changes the
- /// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
- /// </remarks>
- /// <returns>
- /// 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>
- /// <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;
- }
-
- if (damage.Empty)
- {
- return damage;
- }
-
- var before = new BeforeDamageChangedEvent(damage, origin);
- RaiseLocalEvent(uid.Value, ref before);
-
- if (before.Cancelled)
- return null;
-
- // Apply resistances
- if (!ignoreResistances)
- {
- if (damageable.DamageModifierSetId != null &&
- _prototypeManager.Resolve(damageable.DamageModifierSetId, out var modifierSet))
- {
- damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
- }
-
- // TODO DAMAGE
- // byref struct event.
- var ev = new DamageModifyEvent(damage, origin);
- RaiseLocalEvent(uid.Value, ev);
- damage = ev.Damage;
-
- if (damage.Empty)
- {
- return damage;
- }
- }
-
- if (!ignoreGlobalModifiers)
- damage = ApplyUniversalAllModifiers(damage);
-
- var delta = new DamageSpecifier();
- delta.DamageDict.EnsureCapacity(damage.DamageDict.Count);
-
- var dict = damageable.Damage.DamageDict;
- foreach (var (type, value) in damage.DamageDict)
- {
- // CollectionsMarshal my beloved.
- if (!dict.TryGetValue(type, out var oldValue))
- continue;
-
- var newValue = FixedPoint2.Max(FixedPoint2.Zero, oldValue + value);
- if (newValue == oldValue)
- continue;
-
- dict[type] = newValue;
- delta.DamageDict[type] = newValue - oldValue;
- }
-
- if (delta.DamageDict.Count > 0)
- DamageChanged(uid.Value, damageable, delta, interruptsDoAfters, origin);
-
- return delta;
- }
-
- /// <summary>
- /// Applies the two univeral "All" modifiers, if set.
- /// Individual damage source modifiers are set in their respective code.
- /// </summary>
- /// <param name="damage">The damage to be changed.</param>
- public DamageSpecifier ApplyUniversalAllModifiers(DamageSpecifier damage)
- {
- // Checks for changes first since they're unlikely in normal play.
- if (UniversalAllDamageModifier == 1f && UniversalAllHealModifier == 1f)
- return damage;
-
- foreach (var (key, value) in damage.DamageDict)
- {
- if (value == 0)
- continue;
-
- if (value > 0)
- {
- damage.DamageDict[key] *= UniversalAllDamageModifier;
- continue;
- }
-
- if (value < 0)
- {
- damage.DamageDict[key] *= UniversalAllHealModifier;
- }
- }
+namespace Content.Shared.Damage.Systems;
- return damage;
- }
-
- /// <summary>
- /// Sets all damage types supported by a <see cref="DamageableComponent"/> to the specified value.
- /// </summary>
- /// <remakrs>
- /// Does nothing If the given damage value is negative.
- /// </remakrs>
- public void SetAllDamage(EntityUid uid, DamageableComponent component, FixedPoint2 newValue)
- {
- if (newValue < 0)
- {
- // invalid value
- return;
- }
-
- foreach (var type in component.Damage.DamageDict.Keys)
- {
- component.Damage.DamageDict[type] = newValue;
- }
-
- // Setting damage does not count as 'dealing' damage, even if it is set to a larger value, so we pass an
- // empty damage delta.
- DamageChanged(uid, component, new DamageSpecifier());
- }
-
- public void SetDamageModifierSetId(EntityUid uid, string? damageModifierSetId, DamageableComponent? comp = null)
- {
- if (!_damageableQuery.Resolve(uid, ref comp))
- return;
-
- comp.DamageModifierSetId = damageModifierSetId;
- Dirty(uid, comp);
- }
-
- private void DamageableGetState(EntityUid uid, DamageableComponent component, ref ComponentGetState args)
- {
- if (_netMan.IsServer)
- {
- args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageContainerID, component.DamageModifierSetId, component.HealthBarThreshold);
- }
- else
- {
- // avoid mispredicting damage on newly spawned entities.
- args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageContainerID, component.DamageModifierSetId, component.HealthBarThreshold);
- }
- }
-
- private void OnIrradiated(EntityUid uid, DamageableComponent component, OnIrradiatedEvent args)
- {
- var damageValue = FixedPoint2.New(args.TotalRads);
-
- // Radiation should really just be a damage group instead of a list of types.
- DamageSpecifier damage = new();
- foreach (var typeId in component.RadiationDamageTypeIDs)
- {
- damage.DamageDict.Add(typeId, damageValue);
- }
-
- TryChangeDamage(uid, damage, interruptsDoAfters: false, origin: args.Origin);
- }
-
- private void OnRejuvenate(EntityUid uid, DamageableComponent component, RejuvenateEvent args)
- {
- TryComp<MobThresholdsComponent>(uid, out var thresholds);
- _mobThreshold.SetAllowRevives(uid, true, thresholds); // do this so that the state changes when we set the damage
- SetAllDamage(uid, component, 0);
- _mobThreshold.SetAllowRevives(uid, false, thresholds);
- }
-
- private void DamageableHandleState(EntityUid uid, DamageableComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not DamageableComponentState state)
- {
- return;
- }
-
- component.DamageContainerID = state.DamageContainerId;
- component.DamageModifierSetId = state.ModifierSetId;
- component.HealthBarThreshold = state.HealthBarThreshold;
-
- // Has the damage actually changed?
- DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) };
- var delta = newDamage - component.Damage;
- delta.TrimZeros();
-
- if (!delta.Empty)
- {
- component.Damage = newDamage;
- DamageChanged(uid, component, delta);
- }
- }
- }
-
- /// <summary>
- /// Raised before damage is done, so stuff can cancel it if necessary.
- /// </summary>
- [ByRefEvent]
- public record struct BeforeDamageChangedEvent(DamageSpecifier Damage, EntityUid? Origin = null, bool Cancelled = false);
+public sealed partial class DamageableSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly INetManager _netMan = default!;
+ [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;
+
+ public float UniversalAllDamageModifier { get; private set; } = 1f;
+ public float UniversalAllHealModifier { get; private set; } = 1f;
+ public float UniversalMeleeDamageModifier { get; private set; } = 1f;
+ public float UniversalProjectileDamageModifier { get; private set; } = 1f;
+ public float UniversalHitscanDamageModifier { get; private set; } = 1f;
+ public float UniversalReagentDamageModifier { get; private set; } = 1f;
+ public float UniversalReagentHealModifier { get; private set; } = 1f;
+ public float UniversalExplosionDamageModifier { get; private set; } = 1f;
+ public float UniversalThrownDamageModifier { get; private set; } = 1f;
+ public float UniversalTopicalsHealModifier { get; private set; } = 1f;
+ public float UniversalMobDamageModifier { get; private set; } = 1f;
/// <summary>
- /// Raised on an entity when damage is about to be dealt,
- /// in case anything else needs to modify it other than the base
- /// damageable component.
- ///
- /// For example, armor.
+ /// If the damage in a DamageableComponent was changed this function should be called.
/// </summary>
- public sealed class DamageModifyEvent : EntityEventArgs, IInventoryRelayEvent
+ /// <remarks>
+ /// This updates cached damage information, flags the component as dirty, and raises a damage changed event.
+ /// The damage changed event is used by other systems, such as damage thresholds.
+ /// </remarks>
+ private void OnEntityDamageChanged(
+ Entity<DamageableComponent> ent,
+ DamageSpecifier? damageDelta = null,
+ bool interruptsDoAfters = true,
+ EntityUid? origin = null
+ )
{
- // Whenever locational damage is a thing, this should just check only that bit of armour.
- public SlotFlags TargetSlots { get; } = ~SlotFlags.POCKET;
+ ent.Comp.Damage.GetDamagePerGroup(_prototypeManager, ent.Comp.DamagePerGroup);
+ ent.Comp.TotalDamage = ent.Comp.Damage.GetTotal();
+ Dirty(ent);
- public readonly DamageSpecifier OriginalDamage;
- public DamageSpecifier Damage;
- public EntityUid? Origin;
-
- public DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null)
+ if (damageDelta != null && _appearanceQuery.TryGetComponent(ent, out var appearance))
{
- OriginalDamage = damage;
- Damage = damage;
- Origin = origin;
+ _appearance.SetData(
+ ent,
+ DamageVisualizerKeys.DamageUpdateGroups,
+ new DamageVisualizerGroupData(ent.Comp.DamagePerGroup.Keys.ToList()),
+ appearance
+ );
}
+
+ // TODO DAMAGE
+ // byref struct event.
+ RaiseLocalEvent(ent, new DamageChangedEvent(ent.Comp, damageDelta, interruptsDoAfters, origin));
}
- public sealed class DamageChangedEvent : EntityEventArgs
+ private void DamageableGetState(Entity<DamageableComponent> ent, ref ComponentGetState args)
{
- /// <summary>
- /// This is the component whose damage was changed.
- /// </summary>
- /// <remarks>
- /// Given that nearly every component that cares about a change in the damage, needs to know the
- /// current damage values, directly passing this information prevents a lot of duplicate
- /// Owner.TryGetComponent() calls.
- /// </remarks>
- public readonly DamageableComponent Damageable;
-
- /// <summary>
- /// The amount by which the damage has changed. If the damage was set directly to some number, this will be
- /// null.
- /// </summary>
- public readonly DamageSpecifier? DamageDelta;
-
- /// <summary>
- /// Was any of the damage change dealing damage, or was it all healing?
- /// </summary>
- public readonly bool DamageIncreased;
-
- /// <summary>
- /// Does this event interrupt DoAfters?
- /// Note: As provided in the constructor, this *does not* account for DamageIncreased.
- /// As written into the event, this *does* account for DamageIncreased.
- /// </summary>
- public readonly bool InterruptsDoAfters;
-
- /// <summary>
- /// Contains the entity which caused the change in damage, if any was responsible.
- /// </summary>
- public readonly EntityUid? Origin;
-
- public DamageChangedEvent(DamageableComponent damageable, DamageSpecifier? damageDelta, bool interruptsDoAfters, EntityUid? origin)
+ if (_netMan.IsServer)
{
- Damageable = damageable;
- DamageDelta = damageDelta;
- Origin = origin;
-
- if (DamageDelta == null)
- return;
-
- foreach (var damageChange in DamageDelta.DamageDict.Values)
- {
- if (damageChange > 0)
- {
- DamageIncreased = true;
- break;
- }
- }
- InterruptsDoAfters = interruptsDoAfters && DamageIncreased;
+ args.State = new DamageableComponentState(
+ ent.Comp.Damage.DamageDict,
+ ent.Comp.DamageContainerID,
+ ent.Comp.DamageModifierSetId,
+ ent.Comp.HealthBarThreshold
+ );
+ // TODO BODY SYSTEM pass damage onto body system
+ // BOBBY WHEN? 😭
+ // BOBBY SOON 🫡
+
+ return;
}
+
+ // avoid mispredicting damage on newly spawned entities.
+ args.State = new DamageableComponentState(
+ ent.Comp.Damage.DamageDict.ShallowClone(),
+ ent.Comp.DamageContainerID,
+ ent.Comp.DamageModifierSetId,
+ ent.Comp.HealthBarThreshold
+ );
}
}
using Content.Shared.Damage.Components;
-using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs.Components;
-using Content.Shared.FixedPoint;
using Robust.Shared.Timing;
-namespace Content.Shared.Damage;
+namespace Content.Shared.Damage.Systems;
public sealed class PassiveDamageSystem : EntitySystem
{
foreach (var allowedState in comp.AllowedStates)
{
if(allowedState == mobState.CurrentState)
- _damageable.TryChangeDamage(uid, comp.Damage, true, false, damage);
+ _damageable.ChangeDamage((uid, damage), comp.Damage, true, false);
}
}
}
+using Content.Shared.Damage.Components;
using Content.Shared.Projectiles;
-using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Standing;
-using Robust.Shared.Physics.Events;
+using Content.Shared.Weapons.Ranged.Components;
using Robust.Shared.Containers;
+using Robust.Shared.Physics.Events;
-namespace Content.Shared.Damage.Components;
+namespace Content.Shared.Damage.Systems;
public sealed class RequireProjectileTargetSystem : EntitySystem
{
if (!Resolve(uid, ref godmode, false))
return;
- if (TryComp<DamageableComponent>(uid, out var damageable) && godmode.OldDamage != null)
+ if (godmode.OldDamage != null)
{
- _damageable.SetDamage(uid, damageable, godmode.OldDamage);
+ _damageable.SetDamage(uid, godmode.OldDamage);
}
RemComp<GodmodeComponent>(uid);
using Content.Shared.Inventory;
using Content.Shared.Movement.Systems;
-namespace Content.Shared.Damage
+namespace Content.Shared.Damage.Systems;
+
+public sealed class SlowOnDamageSystem : EntitySystem
{
- public sealed class SlowOnDamageSystem : EntitySystem
+ [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
+
+ public override void Initialize()
{
- [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
+ base.Initialize();
- public override void Initialize()
- {
- base.Initialize();
+ SubscribeLocalEvent<SlowOnDamageComponent, DamageChangedEvent>(OnDamageChanged);
+ SubscribeLocalEvent<SlowOnDamageComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
- SubscribeLocalEvent<SlowOnDamageComponent, DamageChangedEvent>(OnDamageChanged);
- SubscribeLocalEvent<SlowOnDamageComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
+ SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, InventoryRelayedEvent<ModifySlowOnDamageSpeedEvent>>(OnModifySpeed);
+ SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ExaminedEvent>(OnExamined);
+ SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ClothingGotEquippedEvent>(OnGotEquipped);
+ SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
- SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, InventoryRelayedEvent<ModifySlowOnDamageSpeedEvent>>(OnModifySpeed);
- SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ExaminedEvent>(OnExamined);
- SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ClothingGotEquippedEvent>(OnGotEquipped);
- SubscribeLocalEvent<ClothingSlowOnDamageModifierComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
+ SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ComponentStartup>(OnIgnoreStartup);
+ SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ComponentShutdown>(OnIgnoreShutdown);
+ SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ModifySlowOnDamageSpeedEvent>(OnIgnoreModifySpeed);
+ }
- SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ComponentStartup>(OnIgnoreStartup);
- SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ComponentShutdown>(OnIgnoreShutdown);
- SubscribeLocalEvent<IgnoreSlowOnDamageComponent, ModifySlowOnDamageSpeedEvent>(OnIgnoreModifySpeed);
- }
+ private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args)
+ {
+ if (!TryComp<DamageableComponent>(uid, out var damage))
+ return;
+
+ if (damage.TotalDamage == FixedPoint2.Zero)
+ return;
- private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args)
+ // Get closest threshold
+ FixedPoint2 closest = FixedPoint2.Zero;
+ var total = damage.TotalDamage;
+ foreach (var thres in component.SpeedModifierThresholds)
{
- if (!TryComp<DamageableComponent>(uid, out var damage))
- return;
-
- if (damage.TotalDamage == FixedPoint2.Zero)
- return;
-
- // Get closest threshold
- FixedPoint2 closest = FixedPoint2.Zero;
- var total = damage.TotalDamage;
- foreach (var thres in component.SpeedModifierThresholds)
- {
- if (total >= thres.Key && thres.Key > closest)
- closest = thres.Key;
- }
-
- if (closest != FixedPoint2.Zero)
- {
- var speed = component.SpeedModifierThresholds[closest];
-
- var ev = new ModifySlowOnDamageSpeedEvent(speed);
- RaiseLocalEvent(uid, ref ev);
- args.ModifySpeed(ev.Speed, ev.Speed);
- }
+ if (total >= thres.Key && thres.Key > closest)
+ closest = thres.Key;
}
- private void OnDamageChanged(EntityUid uid, SlowOnDamageComponent component, DamageChangedEvent args)
+ if (closest != FixedPoint2.Zero)
{
- // We -could- only refresh if it crossed a threshold but that would kind of be a lot of duplicated
- // code and this isn't a super hot path anyway since basically only humans have this
+ var speed = component.SpeedModifierThresholds[closest];
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid);
+ var ev = new ModifySlowOnDamageSpeedEvent(speed);
+ RaiseLocalEvent(uid, ref ev);
+ args.ModifySpeed(ev.Speed, ev.Speed);
}
+ }
- private void OnModifySpeed(Entity<ClothingSlowOnDamageModifierComponent> ent, ref InventoryRelayedEvent<ModifySlowOnDamageSpeedEvent> args)
- {
- var dif = 1 - args.Args.Speed;
- if (dif <= 0)
- return;
+ private void OnDamageChanged(EntityUid uid, SlowOnDamageComponent component, DamageChangedEvent args)
+ {
+ // We -could- only refresh if it crossed a threshold but that would kind of be a lot of duplicated
+ // code and this isn't a super hot path anyway since basically only humans have this
- // reduces the slowness modifier by the given coefficient
- args.Args.Speed += dif * ent.Comp.Modifier;
- }
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid);
+ }
- private void OnExamined(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ExaminedEvent args)
- {
- var msg = Loc.GetString("slow-on-damage-modifier-examine", ("mod", (1 - ent.Comp.Modifier) * 100));
- args.PushMarkup(msg);
- }
+ private void OnModifySpeed(Entity<ClothingSlowOnDamageModifierComponent> ent, ref InventoryRelayedEvent<ModifySlowOnDamageSpeedEvent> args)
+ {
+ var dif = 1 - args.Args.Speed;
+ if (dif <= 0)
+ return;
- private void OnGotEquipped(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ClothingGotEquippedEvent args)
- {
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer);
- }
+ // reduces the slowness modifier by the given coefficient
+ args.Args.Speed += dif * ent.Comp.Modifier;
+ }
- private void OnGotUnequipped(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ClothingGotUnequippedEvent args)
- {
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer);
- }
+ private void OnExamined(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ExaminedEvent args)
+ {
+ var msg = Loc.GetString("slow-on-damage-modifier-examine", ("mod", (1 - ent.Comp.Modifier) * 100));
+ args.PushMarkup(msg);
+ }
- private void OnIgnoreStartup(Entity<IgnoreSlowOnDamageComponent> ent, ref ComponentStartup args)
- {
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent);
- }
+ private void OnGotEquipped(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ClothingGotEquippedEvent args)
+ {
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer);
+ }
- private void OnIgnoreShutdown(Entity<IgnoreSlowOnDamageComponent> ent, ref ComponentShutdown args)
- {
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent);
- }
+ private void OnGotUnequipped(Entity<ClothingSlowOnDamageModifierComponent> ent, ref ClothingGotUnequippedEvent args)
+ {
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer);
+ }
- private void OnIgnoreModifySpeed(Entity<IgnoreSlowOnDamageComponent> ent, ref ModifySlowOnDamageSpeedEvent args)
- {
- args.Speed = 1f;
- }
+ private void OnIgnoreStartup(Entity<IgnoreSlowOnDamageComponent> ent, ref ComponentStartup args)
+ {
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent);
}
- [ByRefEvent]
- public record struct ModifySlowOnDamageSpeedEvent(float Speed) : IInventoryRelayEvent
+ private void OnIgnoreShutdown(Entity<IgnoreSlowOnDamageComponent> ent, ref ComponentShutdown args)
{
- public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent);
}
+
+ private void OnIgnoreModifySpeed(Entity<IgnoreSlowOnDamageComponent> ent, ref ModifySlowOnDamageSpeedEvent args)
+ {
+ args.Speed = 1f;
+ }
+}
+
+[ByRefEvent]
+public record struct ModifySlowOnDamageSpeedEvent(float Speed) : IInventoryRelayEvent
+{
+ public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
}
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Robust.Shared.Serialization;
namespace Content.Shared.Destructible.Thresholds.Triggers;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Prototypes;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
namespace Content.Shared.Destructible.Thresholds.Triggers;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Robust.Shared.Serialization;
namespace Content.Shared.Destructible.Thresholds.Triggers;
using System.Threading.Tasks;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Hands.Components;
using Content.Shared.Tag;
using Robust.Shared.GameStates;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Doors.Components;
using Content.Shared.Emag.Systems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using Robust.Shared.Prototypes;
damageSpec *= args.Scale;
_damageable.TryChangeDamage(
- entity,
+ entity.AsNullable(),
damageSpec,
args.Effect.IgnoreResistances,
- interruptsDoAfters: false,
- damageable: entity.Comp);
+ interruptsDoAfters: false);
}
}
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
public sealed partial class HealthChangeEntityEffectSystem : EntityEffectSystem<DamageableComponent, HealthChange>
{
- [Dependency] private readonly DamageableSystem _damageable = default!;
+ [Dependency] private readonly Damage.Systems.DamageableSystem _damageable = default!;
protected override void Effect(Entity<DamageableComponent> entity, ref EntityEffectEvent<HealthChange> args)
{
damageSpec *= args.Scale;
_damageable.TryChangeDamage(
- entity,
+ entity.AsNullable(),
damageSpec,
args.Effect.IgnoreResistances,
interruptsDoAfters: false);
var damageSpec = new DamageSpecifier(Damage);
- var universalReagentDamageModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentDamageModifier;
- var universalReagentHealModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentHealModifier;
+ var universalReagentDamageModifier = entSys.GetEntitySystem<Damage.Systems.DamageableSystem>().UniversalReagentDamageModifier;
+ var universalReagentHealModifier = entSys.GetEntitySystem<Damage.Systems.DamageableSystem>().UniversalReagentHealModifier;
- damageSpec = entSys.GetEntitySystem<DamageableSystem>().ApplyUniversalAllModifiers(damageSpec);
+ damageSpec = entSys.GetEntitySystem<Damage.Systems.DamageableSystem>().ApplyUniversalAllModifiers(damageSpec);
foreach (var (kind, amount) in damageSpec.DamageDict)
{
using Content.Shared.ActionBlocker;
using Content.Shared.Chat;
using Content.Shared.CombatMode;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Interaction.Events;
-using Content.Shared.Mind;
using Robust.Shared.Player;
using Robust.Shared.Audio.Systems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Popups;
using Content.Shared.Fax.Components;
return;
var damageSpec = faxecute.Damage;
- _damageable.TryChangeDamage(sendEntity, damageSpec);
+ _damageable.ChangeDamage(sendEntity.Value, damageSpec);
_popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-error", ("target", uid)), uid, PopupType.LargeCaution);
return;
using Content.Shared.Flash.Components;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
namespace Content.Shared.Flash;
// Best wait for Ed's status effect system rewrite.
private void OnFlashAttempt(Entity<DamagedByFlashingComponent> ent, ref FlashAttemptEvent args)
{
- _damageable.TryChangeDamage(ent, ent.Comp.FlashDamage);
+ _damageable.ChangeDamage(ent.Owner, ent.Comp.FlashDamage);
// TODO: It would be more logical if different flashes had different power,
// and the damage would be inflicted depending on the strength of the flash.
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Forensics;
using Content.Shared.Contraband;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
+using Content.Shared.Damage.Systems;
using Content.Shared.Electrocution;
using Content.Shared.Explosion;
using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Systems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Destructible;
using Content.Shared.DoAfter;
{
EnsureComp<KitchenSpikeVictimComponent>(args.Target.Value);
- _damageableSystem.TryChangeDamage(args.Target, ent.Comp.ButcherDamage, true);
+ _damageableSystem.ChangeDamage(args.Target.Value, ent.Comp.ButcherDamage, true);
// Log severity for damaging other entities is normally medium.
_logger.Add(LogType.Action,
kitchenSpike.NextDamage += kitchenSpike.DamageInterval;
Dirty(uid, kitchenSpike);
- _damageableSystem.TryChangeDamage(contained, kitchenSpike.TimeDamage, true);
+ _damageableSystem.ChangeDamage(contained.Value, kitchenSpike.TimeDamage, true);
}
}
using Content.Shared.Body.Components;
using Content.Shared.Body.Systems;
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
if (healing.ModifyBloodLevel != 0 && bloodstream != null)
_bloodstreamSystem.TryModifyBloodLevel((target.Owner, bloodstream), healing.ModifyBloodLevel);
- var healed = _damageable.TryChangeDamage(target.Owner, healing.Damage * _damageable.UniversalTopicalsHealModifier, true, origin: args.Args.User);
-
- if (healed == null && healing.BloodlossModifier != 0)
+ if (!_damageable.TryChangeDamage(target.Owner, healing.Damage * _damageable.UniversalTopicalsHealModifier, out var healed, true, origin: args.Args.User) && healing.BloodlossModifier != 0)
return;
- var total = healed?.GetTotal() ?? FixedPoint2.Zero;
+ var total = healed.GetTotal();
// Re-verify that we can heal the damage.
var dontRepeat = false;
using Content.Shared.Actions;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Access.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Clothing;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.DeviceNetwork;
using Content.Shared.DoAfter;
using Content.Shared.Emp;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
namespace Content.Shared.Mobs.Components
{
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage;
using Content.Shared.Damage.ForceSay;
+using Content.Shared.Damage.Systems;
using Content.Shared.Emoting;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.Standing;
-using Robust.Shared.Physics.Systems;
using Robust.Shared.Timing;
namespace Content.Shared.Mobs.Systems;
using System.Linq;
using Content.Shared.Alert;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Events;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Alert;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Rejuvenate;
using Content.Shared.StatusIcon;
-using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
-using Robust.Shared.Utility;
namespace Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Actions;
using Content.Shared.Coordinates;
-using Content.Shared.Damage;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Damage.Systems;
namespace Content.Shared.Polymorph.Systems;
using Content.Shared.Administration.Logs;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
if (ent.Comp.Damage != null)
{
- var damageChanged = _damageableSystem.TryChangeDamage(ent.Owner, ent.Comp.Damage, true, false, origin: args.User);
- _adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(ent.Owner):target} by {damageChanged?.GetTotal()}");
+ var damageChanged = _damageableSystem.ChangeDamage(ent.Owner, ent.Comp.Damage, true, false, origin: args.User);
+ _adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(ent.Owner):target} by {damageChanged.GetTotal()}");
}
else
{
// Repair all damage
- _damageableSystem.SetAllDamage(ent.Owner, damageable, 0);
+ _damageableSystem.SetAllDamage((ent.Owner, damageable), 0);
_adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(ent.Owner):target} back to full health");
}
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Silicons.Borgs.Components;
namespace Content.Shared.Silicons.Borgs;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Serialization;
+using System.Diagnostics.CodeAnalysis;
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.DoAfter;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.NPC.Components;
using Content.Shared.Popups;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Serialization;
-using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Silicons.Bots;
using Content.Shared.Construction.EntitySystems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Destructible;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Gravity;
public sealed class DamageOnTriggerSystem : XOnTriggerSystem<DamageOnTriggerComponent>
{
- [Dependency] private readonly DamageableSystem _damageableSystem = default!;
+ [Dependency] private readonly Damage.Systems.DamageableSystem _damageableSystem = default!;
protected override void OnTrigger(Entity<DamageOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
{
var ev = new BeforeDamageOnTriggerEvent(damage, target);
RaiseLocalEvent(ent.Owner, ref ev);
- args.Handled |= _damageableSystem.TryChangeDamage(target, ev.Damage, ent.Comp.IgnoreResistances, origin: ent.Owner) is not null;
+ args.Handled |= _damageableSystem.TryChangeDamage(target, ev.Damage, ent.Comp.IgnoreResistances, origin: ent.Owner);
}
}
-using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Interaction;
using Content.Shared.Popups;
if (TryComp<DamageableComponent>(ent, out var damageable))
{
var damageSetID = enabled ? ent.Comp.DeployedDamageModifierSetId : ent.Comp.RetractedDamageModifierSetId;
- _damageable.SetDamageModifierSetId(ent, damageSetID, damageable);
+ _damageable.SetDamageModifierSetId((ent, damageable), damageSetID);
}
// Change the turret's fixtures
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Weapons.Hitscan.Components;
using Content.Shared.Weapons.Hitscan.Events;
var dmg = ent.Comp.Damage * _damage.UniversalHitscanDamageModifier;
- var damageDealt = _damage.TryChangeDamage(args.Data.HitEntity, dmg, origin: args.Data.Gun);
-
- if (damageDealt == null)
+ if(!_damage.TryChangeDamage(args.Data.HitEntity.Value, dmg, out var damageDealt, origin: args.Data.Gun))
return;
var damageEvent = new HitscanDamageDealtEvent
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Projectiles;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Whitelist;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Physics.Events;
using Content.Shared.Administration.Logs;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
RaiseLocalEvent(target.Value, attackedEvent);
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
- var damageResult = Damageable.TryChangeDamage(target, modifiedDamage, origin:user, ignoreResistances:resistanceBypass);
- if (damageResult is {Empty: false})
+ if (!Damageable.TryChangeDamage(target.Value, modifiedDamage, out var damageResult, origin:user, ignoreResistances:resistanceBypass))
{
// If the target has stamina and is taking blunt damage, they should also take stamina damage based on their blunt to stamina factor
if (damageResult.DamageDict.TryGetValue("Blunt", out var bluntDamage))
_meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component);
- if (damageResult?.GetTotal() > FixedPoint2.Zero)
+ if (damageResult.GetTotal() > FixedPoint2.Zero)
{
DoDamageEffect(targets, user, targetXform);
}
RaiseLocalEvent(entity, attackedEvent);
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
- var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin: user, ignoreResistances: resistanceBypass);
+ var damageResult = Damageable.ChangeDamage(entity, modifiedDamage, origin: user, ignoreResistances: resistanceBypass);
- if (damageResult != null && damageResult.GetTotal() > FixedPoint2.Zero)
+ if (damageResult.GetTotal() > FixedPoint2.Zero)
{
// If the target has stamina and is taking blunt damage, they should also take stamina damage based on their blunt to stamina factor
if (damageResult.DamageDict.TryGetValue("Blunt", out var bluntDamage))
using Content.Shared.CombatMode;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Hands.EntitySystems;
using System.Linq;
using Content.Shared.Chemistry;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Whitelist;
using Content.Shared.Xenoarchaeology.Artifact.XAE.Components;
using Robust.Shared.Random;
using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Content.Shared.Xenoarchaeology.Artifact.XAT.Components;
using Robust.Shared.Prototypes;
-using Content.Shared.Damage;
+using Content.Shared.Damage.Systems;
using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
using Content.Shared.Popups;