Skip to content

Commit

Permalink
Create Generic DamageOnInteract/Attacked Comps/Systems (#30244)
Browse files Browse the repository at this point in the history
* Everything but the submodule

* stuff I forgot

* heat

* missed lights

* behonky

* LocId

* I guess it was a skill issue?

* predicted audio

* It works with lights now

* Borg equality

* Gorilla gauntlet grants protection from anomaly returned damage when attacking it

* woops, there we go

* NONE

* Use DamageModifierSets, remove Behonker damage

* Reviews dealt with

---------

Co-authored-by: plykiya <[email protected]>
  • Loading branch information
Plykiya and plykiya authored Aug 9, 2024
1 parent 8a4ef69 commit b266c9b
Show file tree
Hide file tree
Showing 25 changed files with 507 additions and 185 deletions.
12 changes: 0 additions & 12 deletions Content.Server/Clothing/Components/GloveHeatResistanceComponent.cs

This file was deleted.

4 changes: 0 additions & 4 deletions Content.Server/Light/Components/PoweredLightComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ public sealed partial class PoweredLightComponent : Component
[DataField("on")]
public bool On = true;

[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier Damage = default!;

[DataField("ignoreGhostsBoo")]
public bool IgnoreGhostsBoo;

Expand Down
45 changes: 7 additions & 38 deletions Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Content.Server.Administration.Logs;
using Content.Server.Clothing.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork;
Expand All @@ -24,6 +23,8 @@
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Robust.Shared.Audio.Systems;
using Content.Shared.Damage.Systems;
using Content.Shared.Damage.Components;

namespace Content.Server.Light.EntitySystems
{
Expand All @@ -33,19 +34,16 @@ namespace Content.Server.Light.EntitySystems
public sealed class PoweredLightSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSystem = default!;
[Dependency] private readonly LightBulbSystem _bulbSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger= default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly PointLightSystem _pointLight = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly DamageOnInteractSystem _damageOnInteractSystem = default!;

private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2);
public const string LightBulbContainer = "light_bulb";
Expand Down Expand Up @@ -106,40 +104,7 @@ private void OnInteractHand(EntityUid uid, PoweredLightComponent light, Interact
if (bulbUid == null)
return;

// check if it's possible to apply burn damage to user
var userUid = args.User;
if (EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb))
{
// get users heat resistance
var res = int.MinValue;
if (_inventory.TryGetSlotEntity(userUid, "gloves", out var slotEntity) &&
TryComp<GloveHeatResistanceComponent>(slotEntity, out var gloves))
{
res = gloves.HeatResistance;
}

// check heat resistance against user
var burnedHand = light.CurrentLit && res < lightBulb.BurningTemperature;
if (burnedHand)
{
var damage = _damageableSystem.TryChangeDamage(userUid, light.Damage, origin: userUid);

// If damage is null then the entity could not take heat damage so they did not get burned.
if (damage != null)
{

var burnMsg = Loc.GetString("powered-light-component-burn-hand");
_popupSystem.PopupEntity(burnMsg, uid, userUid);
_adminLogger.Add(LogType.Damaged, $"{ToPrettyString(args.User):user} burned their hand on {ToPrettyString(args.Target):target} and received {damage.GetTotal():damage} damage");
_audio.PlayEntity(light.BurnHandSound, Filter.Pvs(uid), uid, true);

args.Handled = true;
return;
}
}
}


//removing a broken/burned bulb, so allow instant removal
if(TryComp<LightBulbComponent>(bulbUid.Value, out var bulb) && bulb.State != LightBulbState.Normal)
{
Expand Down Expand Up @@ -435,6 +400,10 @@ private void SetLight(EntityUid uid, bool value, Color? color = null, PoweredLig
if (softness != null)
_pointLight.SetSoftness(uid, (float) softness, pointLight);
}

// light bulbs burn your hands!
if (TryComp<DamageOnInteractComponent>(uid, out var damageOnInteractComp))
_damageOnInteractSystem.SetIsDamageActiveTo((uid, damageOnInteractComp), value);
}

public void ToggleLight(EntityUid uid, PoweredLightComponent? light = null)
Expand Down
14 changes: 0 additions & 14 deletions Content.Shared/Anomaly/Components/AnomalyComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,20 +202,6 @@ public sealed partial class AnomalyComponent : Component
public float GrowingPointMultiplier = 1.5f;
#endregion

/// <summary>
/// The amount of damage dealt when either a player touches the anomaly
/// directly or by hitting the anomaly.
/// </summary>
[DataField(required: true)]
public DamageSpecifier AnomalyContactDamage = default!;

/// <summary>
/// The sound effect played when a player
/// burns themselves on an anomaly via contact.
/// </summary>
[DataField]
public SoundSpecifier AnomalyContactDamageSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");

/// <summary>
/// A prototype entity that appears when an anomaly supercrit collapse.
/// </summary>
Expand Down
26 changes: 0 additions & 26 deletions Content.Shared/Anomaly/SharedAnomalySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public abstract class SharedAnomalySystem : EntitySystem
[Dependency] private readonly INetManager _net = default!;
[Dependency] protected readonly IRobustRandom Random = default!;
[Dependency] protected readonly ISharedAdminLogManager AdminLog = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
Expand All @@ -42,26 +41,10 @@ public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<AnomalyComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<AnomalyComponent, AttackedEvent>(OnAttacked);
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitStartEvent>(OnAnomalyThrowStart);
SubscribeLocalEvent<AnomalyComponent, MeleeThrowOnHitEndEvent>(OnAnomalyThrowEnd);
}

private void OnInteractHand(EntityUid uid, AnomalyComponent component, InteractHandEvent args)
{
DoAnomalyBurnDamage(uid, args.User, component);
args.Handled = true;
}

private void OnAttacked(EntityUid uid, AnomalyComponent component, AttackedEvent args)
{
if (HasComp<CorePoweredThrowerComponent>(args.Used))
return;

DoAnomalyBurnDamage(uid, args.User, component);
}

private void OnAnomalyThrowStart(Entity<AnomalyComponent> ent, ref MeleeThrowOnHitStartEvent args)
{
if (!TryComp<CorePoweredThrowerComponent>(args.Used, out var corePowered) || !TryComp<PhysicsComponent>(ent, out var body))
Expand All @@ -75,15 +58,6 @@ private void OnAnomalyThrowEnd(Entity<AnomalyComponent> ent, ref MeleeThrowOnHit
_physics.SetBodyType(ent, BodyType.Static);
}

public void DoAnomalyBurnDamage(EntityUid source, EntityUid target, AnomalyComponent component)
{
_damageable.TryChangeDamage(target, component.AnomalyContactDamage, true);
if (!Timing.IsFirstTimePredicted || _net.IsServer)
return;
Audio.PlayPvs(component.AnomalyContactDamageSound, source);
Popup.PopupEntity(Loc.GetString("anomaly-component-contact-damage"), target, target);
}

public void DoAnomalyPulse(EntityUid uid, AnomalyComponent? component = null)
{
if (!Resolve(uid, ref component))
Expand Down
47 changes: 47 additions & 0 deletions Content.Shared/Damage/Components/DamageOnAttackedComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;

namespace Content.Shared.Damage.Components;


/// <summary>
/// This component is added to entities that you want to damage the player
/// if the player interacts with it. For example, if a player tries touching
/// a hot light bulb or an anomaly. This damage can be cancelled if the user
/// has a component that protects them from this.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class DamageOnAttackedComponent : Component
{
/// <summary>
/// How much damage to apply to the person making contact
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageSpecifier Damage = default!;

/// <summary>
/// Whether the damage should be resisted by a person's armor values
/// and the <see cref="DamageOnAttackedProtectionComponent"/>
/// </summary>
[DataField]
public bool IgnoreResistances = false;

/// <summary>
/// What kind of localized text should pop up when they interact with the entity
/// </summary>
[DataField]
public LocId? PopupText;

/// <summary>
/// The sound that should be made when interacting with the entity
/// </summary>
[DataField]
public SoundSpecifier InteractSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");

/// <summary>
/// Generic boolean to toggle the damage application on and off
/// This is useful for things that can be toggled on or off, like a stovetop
/// </summary>
[DataField, AutoNetworkedField]
public bool IsDamageActive = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Shared.Inventory;
using Robust.Shared.GameStates;

namespace Content.Shared.Damage.Components;


/// <summary>
/// This component is added to entities to protect them from being damaged
/// when attacking objects with the <see cref="DamageOnAttackedComponent"/>
/// If the entity has sufficient protection, the entity will take no damage.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class DamageOnAttackedProtectionComponent : Component, IClothingSlots
{
/// <summary>
/// How much and what kind of damage to protect the user from
/// when interacting with something with <see cref="DamageOnInteractComponent"/>
/// </summary>
[DataField(required: true)]
public DamageModifierSet DamageProtection = default!;

/// <summary>
/// Only protects if the item is in the correct slot
/// i.e. having gloves in your pocket doesn't protect you, it has to be on your hands
/// Set slots to NONE if it works while you hold the item in your main hand
/// </summary>
[DataField]
public SlotFlags Slots { get; set; } = SlotFlags.WITHOUT_POCKET;
}
47 changes: 47 additions & 0 deletions Content.Shared/Damage/Components/DamageOnInteractComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;

namespace Content.Shared.Damage.Components;


/// <summary>
/// This component is added to entities that you want to damage the player
/// if the player interacts with it. For example, if a player tries touching
/// a hot light bulb or an anomaly. This damage can be cancelled if the user
/// has a component that protects them from this.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class DamageOnInteractComponent : Component
{
/// <summary>
/// How much damage to apply to the person making contact
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageSpecifier Damage = default!;

/// <summary>
/// Whether the damage should be resisted by a person's armor values
/// and the <see cref="DamageOnInteractProtectionComponent"/>
/// </summary>
[DataField]
public bool IgnoreResistances;

/// <summary>
/// What kind of localized text should pop up when they interact with the entity
/// </summary>
[DataField]
public LocId? PopupText;

/// <summary>
/// The sound that should be made when interacting with the entity
/// </summary>
[DataField]
public SoundSpecifier InteractSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");

/// <summary>
/// Generic boolean to toggle the damage application on and off
/// This is useful for things that can be toggled on or off, like a stovetop
/// </summary>
[DataField, AutoNetworkedField]
public bool IsDamageActive = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Shared.Inventory;
using Robust.Shared.GameStates;

namespace Content.Shared.Damage.Components;


/// <summary>
/// This component is added to entities to protect them from being damaged
/// when interacting with objects with the <see cref="DamageOnInteractComponent"/>
/// If the entity has sufficient protection, interaction with the object is not cancelled.
/// This allows the user to do things like remove a lightbulb.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class DamageOnInteractProtectionComponent : Component, IClothingSlots
{
/// <summary>
/// How much and what kind of damage to protect the user from
/// when interacting with something with <see cref="DamageOnInteractComponent"/>
/// </summary>
[DataField(required: true)]
public DamageModifierSet DamageProtection = default!;

/// <summary>
/// Only protects if the item is in the correct slot
/// i.e. having gloves in your pocket doesn't protect you, it has to be on your hands
/// </summary>
[DataField]
public SlotFlags Slots { get; set; } = SlotFlags.GLOVES;
}
Loading

0 comments on commit b266c9b

Please sign in to comment.