diff --git a/Content.Server/Atmos/Components/BarotraumaComponent.cs b/Content.Server/Atmos/Components/BarotraumaComponent.cs index 2e789ba4e1..9dba64a22c 100644 --- a/Content.Server/Atmos/Components/BarotraumaComponent.cs +++ b/Content.Server/Atmos/Components/BarotraumaComponent.cs @@ -21,5 +21,10 @@ namespace Content.Server.Atmos.Components [DataField("maxDamage")] [ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 MaxDamage = 200; + + /// + /// Used to keep track of when damage starts/stops. Useful for logs. + /// + public bool TakingDamage = false; } } diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 7dd99dd3c0..6feb108c76 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -1,7 +1,9 @@ using System; using System.Data; +using Content.Server.Administration.Logs; using Content.Server.Alert; using Content.Server.Atmos.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; @@ -15,6 +17,7 @@ namespace Content.Server.Atmos.EntitySystems { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; private const float UpdateTimer = 1f; @@ -105,6 +108,12 @@ namespace Content.Server.Atmos.EntitySystems // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false); + if (!barotrauma.TakingDamage) + { + barotrauma.TakingDamage = true; + _logSystem.Add(LogType.Barotrauma, $"{barotrauma.Owner} started taking low pressure damage"); + } + if (status == null) break; if (pressure <= Atmospherics.HazardLowPressure) @@ -128,6 +137,12 @@ namespace Content.Server.Atmos.EntitySystems // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false); + if (!barotrauma.TakingDamage) + { + barotrauma.TakingDamage = true; + _logSystem.Add(LogType.Barotrauma, $"{barotrauma.Owner} started taking high pressure damage"); + } + if (status == null) break; if (pressure >= Atmospherics.HazardHighPressure) @@ -141,6 +156,11 @@ namespace Content.Server.Atmos.EntitySystems // Normal pressure. default: + if (barotrauma.TakingDamage) + { + barotrauma.TakingDamage = false; + _logSystem.Add(LogType.Barotrauma, $"{barotrauma.Owner} stopped taking pressure damage"); + } status?.ClearAlertCategory(AlertCategory.Pressure); break; } diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 2bfb1feb35..461e4d1257 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using Content.Server.Administration.Logs; using Content.Server.Alert; using Content.Server.Atmos.Components; using Content.Server.Stunnable; using Content.Server.Temperature.Systems; using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; @@ -26,6 +28,7 @@ namespace Content.Server.Atmos.EntitySystems [Dependency] private readonly StunSystem _stunSystem = default!; [Dependency] private readonly TemperatureSystem _temperatureSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; private const float MinimumFireStacks = -10f; private const float MaximumFireStacks = 20f; @@ -140,6 +143,7 @@ namespace Content.Server.Atmos.EntitySystems if (!flammable.OnFire) return; + _logSystem.Add(LogType.Flammable, $"{flammable.Owner} stopped being on fire damage"); flammable.OnFire = false; flammable.FireStacks = 0; @@ -155,6 +159,7 @@ namespace Content.Server.Atmos.EntitySystems if (flammable.FireStacks > 0 && !flammable.OnFire) { + _logSystem.Add(LogType.Flammable, $"{flammable.Owner} is on fire"); flammable.OnFire = true; } diff --git a/Content.Server/Body/Components/RespiratorComponent.cs b/Content.Server/Body/Components/RespiratorComponent.cs index 00bf65fe84..2f5e5e1ef6 100644 --- a/Content.Server/Body/Components/RespiratorComponent.cs +++ b/Content.Server/Body/Components/RespiratorComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Alert; using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; @@ -8,6 +9,7 @@ using Content.Server.Body.Behavior; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Body.Components; @@ -313,6 +315,9 @@ namespace Content.Server.Body.Components private void TakeSuffocationDamage() { + if (!Suffocating) + EntitySystem.Get().Add(LogType.Asphyxiation, $"{Owner} started suffocating"); + Suffocating = true; if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent)) @@ -325,6 +330,9 @@ namespace Content.Server.Body.Components private void StopSuffocation() { + if (Suffocating) + EntitySystem.Get().Add(LogType.Asphyxiation, $"{Owner} stopped suffocating"); + Suffocating = false; if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent)) diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index a9ba9f309d..b502c7ca2a 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -1,12 +1,14 @@ using System.Linq; using Content.Server.Act; using Content.Server.Administration; +using Content.Server.Administration.Logs; using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.Hands.Components; using Content.Server.Items; using Content.Server.Players; using Content.Server.Popups; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Popups; @@ -81,6 +83,8 @@ namespace Content.Server.Chat.Commands //TODO: needs to check if the mob is actually alive //TODO: maybe set a suicided flag to prevent resurrection? + EntitySystem.Get().Add(LogType.Suicide, $"{player.AttachedEntity} is committing suicide"); + // Held item suicide var handsComponent = owner.GetComponent(); var itemComponent = handsComponent.GetActiveHand; diff --git a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs index afa5d947f0..f0db7dc5f0 100644 --- a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs @@ -1,7 +1,9 @@ using System; +using Content.Server.Administration.Logs; using Content.Server.Damage.Components; using Content.Server.Stunnable; using Content.Server.Stunnable.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Audio; using Content.Shared.Damage; using Content.Shared.Stunnable; @@ -23,6 +25,7 @@ namespace Content.Server.Damage.Systems [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly StunSystem _stunSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -50,7 +53,11 @@ namespace Content.Server.Damage.Systems _stunSystem.TryStun(uid, TimeSpan.FromSeconds(component.StunSeconds)); var damageScale = (speed / component.MinimumSpeed) * component.Factor; - _damageableSystem.TryChangeDamage(uid, component.Damage * damageScale); + + var dmg = _damageableSystem.TryChangeDamage(uid, component.Damage * damageScale); + + if (dmg != null) + _logSystem.Add(LogType.Damaged, $"{component.Owner} took {dmg.Total} damage from a high speed collision"); } } } diff --git a/Content.Server/Damage/Systems/DamageOnLandSystem.cs b/Content.Server/Damage/Systems/DamageOnLandSystem.cs index ba2b789a1b..48dbe4f997 100644 --- a/Content.Server/Damage/Systems/DamageOnLandSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnLandSystem.cs @@ -1,4 +1,6 @@ +using Content.Server.Administration.Logs; using Content.Server.Damage.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Throwing; using Robust.Shared.GameObjects; @@ -9,6 +11,7 @@ namespace Content.Server.Damage.Systems public sealed class DamageOnLandSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -18,7 +21,11 @@ namespace Content.Server.Damage.Systems private void DamageOnLand(EntityUid uid, DamageOnLandComponent component, LandEvent args) { - _damageableSystem.TryChangeDamage(uid, component.Damage, component.IgnoreResistances); + var dmg = _damageableSystem.TryChangeDamage(uid, component.Damage, component.IgnoreResistances); + if (dmg == null) + return; + + _logSystem.Add(LogType.Landed, $"{component.Owner} received {dmg.Total} damage from landing"); } } } diff --git a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs index 225ff9ae76..9b75e34e44 100644 --- a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs @@ -1,5 +1,7 @@ +using Content.Server.Administration.Logs; using Content.Server.Damage.Components; using Content.Server.Tools.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Interaction; using Robust.Shared.GameObjects; @@ -10,6 +12,7 @@ namespace Content.Server.Damage.Systems public class DamageOnToolInteractSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -27,18 +30,25 @@ namespace Content.Server.Damage.Systems && args.Used.TryGetComponent(out var welder) && welder.Lit) { - _damageableSystem.TryChangeDamage(args.Target.Uid, weldingDamage); - args.Handled = true; - return; - } + var dmg = _damageableSystem.TryChangeDamage(args.Target.Uid, weldingDamage); - if (component.DefaultDamage is {} damage + if (dmg != null) + _logSystem.Add(LogType.Damaged, + $"{args.User} used {args.Used} as a welder to deal {dmg.Total} damage to {args.Target}"); + + args.Handled = true; + } + else if (component.DefaultDamage is {} damage && args.Used.TryGetComponent(out var tool) && tool.Qualities.ContainsAny(component.Tools)) { - _damageableSystem.TryChangeDamage(args.Target.Uid, damage); + var dmg = _damageableSystem.TryChangeDamage(args.Target.Uid, damage); + + if (dmg != null) + _logSystem.Add(LogType.Damaged, + $"{args.User} used {args.Used} as a tool to deal {dmg.Total} damage to {args.Target}"); + args.Handled = true; - return; } } } diff --git a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs index 900ded4802..98ea41dd0b 100644 --- a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs +++ b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs @@ -1,4 +1,6 @@ +using Content.Server.Administration.Logs; using Content.Server.Damage.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Throwing; using Robust.Shared.GameObjects; @@ -9,7 +11,8 @@ namespace Content.Server.Damage.Systems public class DamageOtherOnHitSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; - + [Dependency] private readonly AdminLogSystem _logSystem = default!; + public override void Initialize() { SubscribeLocalEvent(OnDoHit); @@ -17,7 +20,9 @@ namespace Content.Server.Damage.Systems private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args) { - _damageableSystem.TryChangeDamage(args.Target.Uid, component.Damage, component.IgnoreResistances); + var dmg = _damageableSystem.TryChangeDamage(args.Target.Uid, component.Damage, component.IgnoreResistances); + if (dmg != null) + _logSystem.Add(LogType.ThrowHit, $"{args.Target} received {dmg.Total} damage from collision"); } } } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 5d4f95eaac..1a09b24523 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Content.Server.Administration.Logs; using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.NodeGroups; @@ -8,6 +9,7 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Power.NodeGroups; using Content.Server.Window; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -46,6 +48,7 @@ namespace Content.Server.Electrocution [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; private const string StatusEffectKey = "Electrocution"; private const string DamageType = "Shock"; @@ -104,7 +107,10 @@ namespace Content.Server.Electrocution _prototypeManager.Index(DamageType), (int) finished.AccumulatedDamage); - _damageableSystem.TryChangeDamage(finished.Electrocuting, damage); + var actual = _damageableSystem.TryChangeDamage(finished.Electrocuting, damage); + if (actual != null) + _logSystem.Add(LogType.Electrocution, + $"{finished.Owner} took {actual.Total} powered electrocution damage"); } EntityManager.DeleteEntity(uid); @@ -337,9 +343,15 @@ namespace Content.Server.Electrocution // TODO: Sparks here. if(shockDamage is {} dmg) - _damageableSystem.TryChangeDamage(uid, + { + var actual = _damageableSystem.TryChangeDamage(uid, new DamageSpecifier(_prototypeManager.Index(DamageType), dmg)); + if (actual != null) + _logSystem.Add(LogType.Electrocution, + $"{statusEffects.Owner} took {actual.Total} powered electrocution damage"); + } + _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, statusEffects, alerts); _jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, JitterAmplitude, JitterFrequency, true, statusEffects, alerts); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 3e1ebd1d74..ff721d0c72 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Camera; using Content.Server.Explosion.Components; using Content.Shared.Acts; +using Content.Shared.Administration.Logs; using Content.Shared.Interaction.Helpers; using Content.Shared.Maps; using Content.Shared.Physics; @@ -48,6 +50,7 @@ namespace Content.Server.Explosion.EntitySystems [Dependency] private readonly ActSystem _acts = default!; [Dependency] private readonly EffectSystem _effects = default!; [Dependency] private readonly TriggerSystem _triggers = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; private bool IgnoreExplosivePassable(IEntity e) { @@ -335,6 +338,9 @@ namespace Content.Server.Explosion.EntitySystems return; } + _logSystem.Add(LogType.Damaged, LogImpact.High , + $"Spawned explosion at {epicenter} with range {devastationRange}/{heavyImpactRange}/{lightImpactRange}/{flashRange}"); + var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0); var epicenterMapPos = epicenter.ToMapPos(EntityManager); var boundingBox = new Box2(epicenterMapPos - new Vector2(maxRange, maxRange), diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 6fc0496b96..640214e96c 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Items; using Content.Server.Stack; using Content.Server.Storage.Components; using Content.Server.Throwing; +using Content.Shared.ActionBlocker; using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Hands.Components; @@ -35,6 +36,7 @@ namespace Content.Server.Hands.Systems [Dependency] private readonly InteractionSystem _interactionSystem = default!; [Dependency] private readonly StackSystem _stackSystem = default!; [Dependency] private readonly HandVirtualItemSystem _virtualItemSystem = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; public override void Initialize() { @@ -223,7 +225,7 @@ namespace Content.Server.Hands.Systems playerEnt.IsInContainer() || !playerEnt.TryGetComponent(out SharedHandsComponent? hands) || !hands.TryGetActiveHeldEntity(out var throwEnt) || - !_interactionSystem.TryThrowInteraction(hands.Owner, throwEnt)) + !_actionBlockerSystem.CanThrow(playerEnt.Uid)) return false; if (throwEnt.TryGetComponent(out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually) diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index c51ae60002..3dfaa51d7b 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -22,6 +22,8 @@ using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Maths; using Content.Shared.Audio; +using Content.Server.Administration.Logs; +using Content.Shared.Administration.Logs; namespace Content.Server.Light.EntitySystems { @@ -35,6 +37,7 @@ namespace Content.Server.Light.EntitySystems [Dependency] private readonly SharedAmbientSoundSystem _ambientSystem = default!; [Dependency] private readonly LightBulbSystem _bulbSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2); @@ -112,7 +115,13 @@ namespace Content.Server.Light.EntitySystems // apply damage to users hands and show message with sound var burnMsg = Loc.GetString("powered-light-component-burn-hand"); _popupSystem.PopupEntity(burnMsg, uid, Filter.Entities(userUid)); - _damageableSystem.TryChangeDamage(userUid, light.Damage); + + var damage = _damageableSystem.TryChangeDamage(userUid, light.Damage); + + if (damage != null) + _logSystem.Add(LogType.Damaged, + $"{args.User} burned their hand on {args.Target} and received {damage.Total} damage"); + SoundSystem.Play(Filter.Pvs(uid), light.BurnHandSound.GetSound(), uid); args.Handled = true; diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index 7bb215021a..c95db5e3ef 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; +using Content.Server.Administration.Logs; using Content.Server.Stack; using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Interaction; @@ -62,7 +64,15 @@ namespace Content.Server.Medical.Components return true; } - EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, Damage, true); + var healed = EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, Damage, true); + + if (healed == null) + return true; + + if (eventArgs.Target != eventArgs.User) + EntitySystem.Get().Add(LogType.Healed, $"{eventArgs.User} healed {eventArgs.Target} for {healed.Total} damage"); + else + EntitySystem.Get().Add(LogType.Healed, $"{eventArgs.User} healed themselves for {healed.Total} damage"); return true; } diff --git a/Content.Server/Nutrition/Components/HungerComponent.cs b/Content.Server/Nutrition/Components/HungerComponent.cs index 1b2b1d31fd..bae37a5c0a 100644 --- a/Content.Server/Nutrition/Components/HungerComponent.cs +++ b/Content.Server/Nutrition/Components/HungerComponent.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using Content.Server.Administration.Logs; using Content.Server.Alert; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.MobState.Components; @@ -203,6 +205,11 @@ namespace Content.Server.Nutrition.Components // _trySound(calculatedThreshold); if (calculatedHungerThreshold != _currentHungerThreshold) { + if (_currentHungerThreshold == HungerThreshold.Dead) + EntitySystem.Get().Add(LogType.Hunger, $"{Owner} has stopped starving"); + else if (calculatedHungerThreshold == HungerThreshold.Dead) + EntitySystem.Get().Add(LogType.Hunger, $"{Owner} has started starving"); + _currentHungerThreshold = calculatedHungerThreshold; HungerThresholdEffect(); Dirty(); diff --git a/Content.Server/Nutrition/Components/ThirstComponent.cs b/Content.Server/Nutrition/Components/ThirstComponent.cs index ca7f28de31..66e7e80789 100644 --- a/Content.Server/Nutrition/Components/ThirstComponent.cs +++ b/Content.Server/Nutrition/Components/ThirstComponent.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using Content.Server.Administration.Logs; using Content.Server.Alert; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.MobState.Components; @@ -200,6 +202,11 @@ namespace Content.Server.Nutrition.Components // _trySound(calculatedThreshold); if (calculatedThirstThreshold != _currentThirstThreshold) { + if (_currentThirstThreshold == ThirstThreshold.Dead) + EntitySystem.Get().Add(LogType.Thirst, $"{Owner} has stopped taking dehydration damage"); + else if (calculatedThirstThreshold == ThirstThreshold.Dead) + EntitySystem.Get().Add(LogType.Thirst, $"{Owner} has started taking dehydration damage"); + _currentThirstThreshold = calculatedThirstThreshold; ThirstThresholdEffect(); Dirty(); diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 982f178c2c..fa5d2a4d88 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -57,9 +57,8 @@ namespace Content.Server.Projectiles component.DamagedEntity = true; if (dmg is not null && EntityManager.TryGetEntity(component.Shooter, out var shooter)) - _adminLogSystem.Add(LogType.BulletHit, LogImpact.Low, $"Bullet shot by {shooter} hit {otherEntity}"); - // "DamagedEntity" is misleading. Hit entity may be more accurate, as the damage may have been resisted - // by resistance sets. + _adminLogSystem.Add(LogType.BulletHit, LogImpact.Low, + $"Projectile {component.Owner} shot by {shooter} hit {otherEntity} and dealt {dmg.Total} damage"); } // Damaging it can delete it diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index bb5b2d7921..a5471ea677 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -1,5 +1,7 @@ +using Content.Server.Administration.Logs; using Content.Server.Tools; using Content.Server.Tools.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Interaction; using Content.Shared.Popups; @@ -14,6 +16,7 @@ namespace Content.Server.Repairable { [Dependency] private readonly ToolSystem _toolSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -32,6 +35,7 @@ namespace Content.Server.Repairable // Repair all damage _damageableSystem.SetAllDamage(damageable, 0); + _logSystem.Add(LogType.Healed, $"{args.User} repaired ${uid} back to full health"); component.Owner.PopupMessage(args.User, Loc.GetString("comp-repairable-repair", diff --git a/Content.Server/Temperature/Components/TemperatureComponent.cs b/Content.Server/Temperature/Components/TemperatureComponent.cs index 9cae7cb7ee..dca4e61a0c 100644 --- a/Content.Server/Temperature/Components/TemperatureComponent.cs +++ b/Content.Server/Temperature/Components/TemperatureComponent.cs @@ -70,5 +70,10 @@ namespace Content.Server.Temperature.Components [DataField("damageCap")] [ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 DamageCap = FixedPoint2.New(8); + + /// + /// Used to keep track of when damage starts/stops. Useful for logs. + /// + public bool TakingDamage = false; } } diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index e65845932c..0b27610d93 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Alert; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Temperature.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.FixedPoint; @@ -17,6 +19,7 @@ namespace Content.Server.Temperature.Systems { [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; /// /// All the components that will have their damage updated at the end of the tick. @@ -160,17 +163,34 @@ namespace Content.Server.Temperature.Systems if (temperature.CurrentTemperature >= temperature.HeatDamageThreshold) { + if (!temperature.TakingDamage) + { + _logSystem.Add(LogType.Temperature, $"{temperature.Owner} started taking high temperature damage"); + temperature.TakingDamage = true; + } + var diff = Math.Abs(temperature.CurrentTemperature - temperature.HeatDamageThreshold); var tempDamage = c / (1 + a * Math.Pow(Math.E, -heatK * diff)) - y; _damageableSystem.TryChangeDamage(uid, temperature.HeatDamage * tempDamage); } else if (temperature.CurrentTemperature <= temperature.ColdDamageThreshold) { + if (!temperature.TakingDamage) + { + _logSystem.Add(LogType.Temperature, $"{temperature.Owner} started taking low temperature damage"); + temperature.TakingDamage = true; + } + var diff = Math.Abs(temperature.CurrentTemperature - temperature.ColdDamageThreshold); var tempDamage = Math.Sqrt(diff * (Math.Pow(temperature.DamageCap.Double(), 2) / temperature.ColdDamageThreshold)); _damageableSystem.TryChangeDamage(uid, temperature.ColdDamage * tempDamage); } + else if (temperature.TakingDamage) + { + _logSystem.Add(LogType.Temperature, $"{temperature.Owner} stopped taking temperature damage"); + temperature.TakingDamage = false; + } } private void OnTemperatureChangeAttempt(EntityUid uid, TemperatureProtectionComponent component, ModifyChangedTemperatureEvent args) diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index 2ef7da23fe..1c742b3ab6 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Body.Components; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.EntitySystems; using Content.Server.Cooldown; using Content.Server.Weapon.Melee.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Damage; using Content.Shared.Hands; using Content.Shared.Interaction; @@ -28,6 +30,7 @@ namespace Content.Server.Weapon.Melee [Dependency] private IGameTiming _gameTiming = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private SolutionContainerSystem _solutionsSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -92,8 +95,19 @@ namespace Content.Server.Weapon.Melee RaiseLocalEvent(target.Uid, new AttackedEvent(args.Used, args.User, args.ClickLocation)); - _damageableSystem.TryChangeDamage(target.Uid, + var damage = _damageableSystem.TryChangeDamage(target.Uid, DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList)); + + if (damage != null) + { + if (args.Used == args.User) + _logSystem.Add(LogType.MeleeHit, + $"{args.User} melee attacked {args.TargetEntity} using their hands and dealt {damage.Total} damage"); + else + _logSystem.Add(LogType.MeleeHit, + $"{args.User} melee attacked {args.TargetEntity} using {args.Used} and dealt {damage.Total} damage"); + } + SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target); } } @@ -160,8 +174,18 @@ namespace Content.Server.Weapon.Melee { RaiseLocalEvent(entity.Uid, new AttackedEvent(args.Used, args.User, args.ClickLocation)); - _damageableSystem.TryChangeDamage(entity.Uid, - DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList)); + var damage = _damageableSystem.TryChangeDamage(entity.Uid, + DamageSpecifier.ApplyModifierSets(comp.Damage, hitEvent.ModifiersList)); + + if (damage != null) + { + if (args.Used == args.User) + _logSystem.Add(LogType.MeleeHit, + $"{args.User} melee attacked {entity} using their hands and dealt {damage.Total} damage"); + else + _logSystem.Add(LogType.MeleeHit, + $"{args.User} melee attacked {entity} using {args.Used} and dealt {damage.Total} damage"); + } } } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs index 053a688ee3..a4a2e573e8 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs @@ -2,9 +2,11 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Content.Server.Administration.Logs; using Content.Server.Camera; using Content.Server.Projectiles.Components; using Content.Server.Weapon.Ranged.Ammunition.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Audio; using Content.Shared.Damage; using Content.Shared.Examine; @@ -396,7 +398,10 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components var result = rayCastResults[0]; var distance = result.Distance; hitscan.FireEffects(shooter, distance, angle, result.HitEntity); - EntitySystem.Get().TryChangeDamage(result.HitEntity.Uid, hitscan.Damage); + var dmg = EntitySystem.Get().TryChangeDamage(result.HitEntity.Uid, hitscan.Damage); + if (dmg != null) + EntitySystem.Get().Add(LogType.HitScanHit, + $"{shooter} hit {result.HitEntity} using {hitscan.Owner} and dealt {dmg.Total} damage"); } else { diff --git a/Content.Shared/Administration/Logs/LogType.cs b/Content.Shared/Administration/Logs/LogType.cs index 00a5b80355..b128de944d 100644 --- a/Content.Shared/Administration/Logs/LogType.cs +++ b/Content.Shared/Administration/Logs/LogType.cs @@ -40,5 +40,17 @@ public enum LogType Pickup = 36, Drop = 37, BulletHit = 38, + MeleeHit = 41, + HitScanHit = 42, + Suicide = 43, + Explosion = 44, + Radiation = 45, + Barotrauma = 46, + Flammable = 47, + Asphyxiation = 48, + Temperature = 49, + Hunger = 50, + Thirst = 51, + Electrocution = 52, CrayonDraw = 39, } diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index dd0e756c0e..17ad591255 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.ViewVariables; +using Content.Shared.Administration.Logs; namespace Content.Shared.Damage { @@ -93,7 +94,11 @@ namespace Content.Shared.Damage damage.DamageDict.Add(typeID, damageValue); } - EntitySystem.Get().TryChangeDamage(OwnerUid, damage); + var actual = EntitySystem.Get().TryChangeDamage(OwnerUid, damage); + + // should logging be disabled during rad storms? a lot of entities are going to be damaged. + if (actual != null) + EntitySystem.Get().Add(LogType.Radiation, $"{Owner} took {actual.Total} radiation damage"); } // TODO EXPLOSION Remove this. @@ -114,7 +119,11 @@ namespace Content.Shared.Damage damage.DamageDict.Add(typeID, damageValue); } - EntitySystem.Get().TryChangeDamage(OwnerUid, damage); + var actual = EntitySystem.Get().TryChangeDamage(OwnerUid, damage); + + // will logging handle nukes? + if (actual != null) + EntitySystem.Get().Add(LogType.Explosion, $"{Owner} took {actual.Total} explosion damage"); } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index d757cb4616..ff9c05c425 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -23,7 +23,10 @@ namespace Content.Shared.Damage SubscribeLocalEvent(DamageableGetState); } - protected virtual void SetTotalDamage(DamageableComponent damageable, FixedPoint2 @new) + /// + /// Update the total damage value and optionally add to admin logs + /// + protected virtual void SetTotalDamage(DamageableComponent damageable, FixedPoint2 @new, bool logChange) { var owner = damageable.Owner; var old = damageable.TotalDamage; @@ -33,6 +36,11 @@ namespace Content.Shared.Damage return; } + damageable.TotalDamage = @new; + + if (!logChange) + return; + LogType logType; string type; FixedPoint2 change; @@ -52,7 +60,6 @@ namespace Content.Shared.Damage _logs.Add(logType, $"{owner} {type} {change} damage. Old: {old} | New: {@new}"); - damageable.TotalDamage = @new; } /// @@ -103,7 +110,7 @@ namespace Content.Shared.Damage public void SetDamage(DamageableComponent damageable, DamageSpecifier damage) { damageable.Damage = damage; - DamageChanged(damageable); + DamageChanged(damageable, false); } /// @@ -113,10 +120,11 @@ namespace Content.Shared.Damage /// 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. /// - public void DamageChanged(DamageableComponent component, DamageSpecifier? damageDelta = null, bool interruptsDoAfters = true) + public void DamageChanged(DamageableComponent component, bool logChange, DamageSpecifier? damageDelta = null, + bool interruptsDoAfters = true) { component.DamagePerGroup = component.Damage.GetDamagePerGroup(); - SetTotalDamage(component, component.Damage.Total); + SetTotalDamage(component, component.Damage.Total, logChange); component.Dirty(); if (EntityManager.TryGetComponent(component.OwnerUid, out var appearance) && damageDelta != null) @@ -136,7 +144,8 @@ namespace Content.Shared.Damage /// Returns a with information about the actual damage changes. This will be /// null if the user had no applicable components that can take damage. /// - public DamageSpecifier? TryChangeDamage(EntityUid uid, DamageSpecifier damage, bool ignoreResistances = false, bool interruptsDoAfters = true) + public DamageSpecifier? TryChangeDamage(EntityUid uid, DamageSpecifier damage, bool ignoreResistances = false, + bool interruptsDoAfters = true, bool logChange = false) { if (!EntityManager.TryGetComponent(uid, out var damageable)) { @@ -185,7 +194,7 @@ namespace Content.Shared.Damage if (!delta.Empty) { - DamageChanged(damageable, delta, interruptsDoAfters); + DamageChanged(damageable, logChange, delta, interruptsDoAfters); } return delta; @@ -212,7 +221,7 @@ namespace Content.Shared.Damage // 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(component, new DamageSpecifier()); + DamageChanged(component, false, new DamageSpecifier()); } private void DamageableGetState(EntityUid uid, DamageableComponent component, ref ComponentGetState args) @@ -237,7 +246,7 @@ namespace Content.Shared.Damage if (!delta.Empty) { component.Damage = newDamage; - DamageChanged(component, delta); + DamageChanged(component, false, delta); } } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 79c47c13ba..c5e3bf3cba 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -543,18 +543,6 @@ namespace Content.Shared.Interaction #endregion #region Throw - /// - /// Activates the Throw behavior of an object - /// Verifies that the user is capable of doing the throw interaction first - /// - public bool TryThrowInteraction(IEntity user, IEntity item) - { - if (user == null || item == null || !_actionBlockerSystem.CanThrow(user.Uid)) return false; - - ThrownInteraction(user, item); - return true; - } - /// /// Calls Thrown on all components that implement the IThrown interface /// on an entity that has been thrown. diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs index a750fe4fa0..3da0b52c10 100644 --- a/Content.Shared/Throwing/ThrownItemSystem.cs +++ b/Content.Shared/Throwing/ThrownItemSystem.cs @@ -1,17 +1,12 @@ -using System.Collections.Generic; -using System.Linq; using Content.Shared.Administration.Logs; -using Content.Shared.CCVar; using Content.Shared.Hands.Components; using Content.Shared.Physics; using Content.Shared.Physics.Pull; -using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Log; -using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; @@ -128,13 +123,12 @@ namespace Content.Shared.Throwing return; } - var landMsg = new LandEvent {User = thrownItem.Thrower?.Uid}; - RaiseLocalEvent(landing.Uid, landMsg, false); - // Assume it's uninteresting if it has no thrower. For now anyway. if (thrownItem.Thrower is not null) _adminLogSystem.Add(LogType.Landed, LogImpact.Low, $"{landing} thrown by {thrownItem.Thrower:thrower} landed."); + var landMsg = new LandEvent {User = thrownItem.Thrower?.Uid}; + RaiseLocalEvent(landing.Uid, landMsg, false); } /// @@ -142,11 +136,12 @@ namespace Content.Shared.Throwing /// public void ThrowCollideInteraction(IEntity? user, IPhysBody thrown, IPhysBody target) { + if (user is not null) + _adminLogSystem.Add(LogType.ThrowHit, LogImpact.Low, + $"{thrown.Owner:thrown} thrown by {user:thrower} hit {target.Owner:target}."); // TODO: Just pass in the bodies directly RaiseLocalEvent(target.Owner.Uid, new ThrowHitByEvent(user, thrown.Owner, target.Owner)); RaiseLocalEvent(thrown.Owner.Uid, new ThrowDoHitEvent(user, thrown.Owner, target.Owner)); - if (user is not null) - _adminLogSystem.Add(LogType.ThrowHit, LogImpact.Low, $"{thrown.Owner:thrown} thrown by {user:thrower} hit {target.Owner:target}."); } } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index 04a8d1a0d1..3340b97874 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -73,18 +73,27 @@ namespace Content.Shared.Verbs } } - public void LogVerb(Verb verb, EntityUid user, EntityUid target, bool forced) + public void LogVerb(Verb verb, EntityUid userUid, EntityUid targetUid, bool forced) { // first get the held item. again. - EntityUid? used = null; - if (EntityManager.TryGetComponent(user, out SharedHandsComponent? hands)) + EntityUid? usedUid = null; + if (EntityManager.TryGetComponent(userUid, out SharedHandsComponent? hands)) { hands.TryGetActiveHeldEntity(out var useEntityd); - used = useEntityd?.Uid; - if (used != null && EntityManager.TryGetComponent(used.Value, out HandVirtualItemComponent? pull)) - used = pull.BlockingEntity; + usedUid = useEntityd?.Uid; + if (usedUid != null && EntityManager.TryGetComponent(usedUid.Value, out HandVirtualItemComponent? pull)) + usedUid = pull.BlockingEntity; } + // get all the entities + if (!EntityManager.TryGetEntity(userUid, out var user) || + !EntityManager.TryGetEntity(targetUid, out var target)) + return; + + IEntity? used = null; + if (usedUid != null) + EntityManager.TryGetEntity(usedUid.Value, out used); + // then prepare the basic log message body var verbText = $"{verb.Category?.Text} {verb.Text}".Trim(); var logText = forced