diff --git a/Content.Shared/Damage/Components/DamageOnInteractComponent.cs b/Content.Shared/Damage/Components/DamageOnInteractComponent.cs
index 9487dec8ef..d5465f19bd 100644
--- a/Content.Shared/Damage/Components/DamageOnInteractComponent.cs
+++ b/Content.Shared/Damage/Components/DamageOnInteractComponent.cs
@@ -44,4 +44,46 @@ public sealed partial class DamageOnInteractComponent : Component
///
[DataField, AutoNetworkedField]
public bool IsDamageActive = true;
+
+ ///
+ /// Whether the thing should be thrown from its current position when they interact with the entity
+ ///
+ [DataField]
+ public bool Throw = false;
+
+ ///
+ /// The speed applied to the thing when it is thrown
+ ///
+ [DataField]
+ public int ThrowSpeed = 10;
+
+ ///
+ /// Time between being able to interact with this entity
+ ///
+ [DataField]
+ public uint InteractTimer = 0;
+
+ ///
+ /// Tracks the last time this entity was interacted with, but only if the interaction resulted in the user taking damage
+ ///
+ [DataField]
+ public TimeSpan LastInteraction = TimeSpan.Zero;
+
+ ///
+ /// Tracks the time that this entity can be interacted with, but only if the interaction resulted in the user taking damage
+ ///
+ [DataField]
+ public TimeSpan NextInteraction = TimeSpan.Zero;
+
+ ///
+ /// Probability that the user will be stunned when they interact with with this entity and took damage
+ ///
+ [DataField]
+ public float StunChance = 0.0f;
+
+ ///
+ /// Duration, in seconds, of the stun applied to the user when they interact with the entity and took damage
+ ///
+ [DataField]
+ public float StunSeconds = 0.0f;
}
diff --git a/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs b/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs
index cc3b3f6d5d..0f43e93abf 100644
--- a/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs
+++ b/Content.Shared/Damage/Systems/DamageOnInteractSystem.cs
@@ -4,9 +4,15 @@ using Content.Shared.Database;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Popups;
+using Robust.Shared.Random;
+using Content.Shared.Throwing;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Timing;
+using Content.Shared.Random;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Effects;
+using Content.Shared.Stunnable;
namespace Content.Shared.Damage.Systems;
@@ -17,6 +23,10 @@ public sealed class DamageOnInteractSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
+ [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly SharedStunSystem _stun = default!;
public override void Initialize()
{
@@ -35,6 +45,13 @@ public sealed class DamageOnInteractSystem : EntitySystem
/// Contains the user that interacted with the entity
private void OnHandInteract(Entity entity, ref InteractHandEvent args)
{
+ // Stop the interaction if the user attempts to interact with the object before the timer is finished
+ if (_gameTiming.CurTime < entity.Comp.NextInteraction)
+ {
+ args.Handled = true;
+ return;
+ }
+
if (!entity.Comp.IsDamageActive)
return;
@@ -47,9 +64,8 @@ public sealed class DamageOnInteractSystem : EntitySystem
// or checking the entity for the comp itself if the inventory didn't work
if (protectiveEntity.Comp == null && TryComp(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
@@ -59,17 +75,31 @@ public sealed class DamageOnInteractSystem : EntitySystem
}
}
- totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, origin: args.Target);
+ totalDamage = _damageableSystem.TryChangeDamage(args.User, totalDamage, origin: args.Target);
if (totalDamage != null && totalDamage.AnyPositive())
{
+ // Record this interaction and determine when a user is allowed to interact with this entity again
+ entity.Comp.LastInteraction = _gameTiming.CurTime;
+ entity.Comp.NextInteraction = _gameTiming.CurTime + TimeSpan.FromSeconds(entity.Comp.InteractTimer);
+
args.Handled = true;
_adminLogger.Add(LogType.Damaged, $"{ToPrettyString(args.User):user} injured their hand by interacting with {ToPrettyString(args.Target):target} and received {totalDamage.GetTotal():damage} damage");
_audioSystem.PlayPredicted(entity.Comp.InteractSound, args.Target, args.User);
if (entity.Comp.PopupText != null)
_popupSystem.PopupClient(Loc.GetString(entity.Comp.PopupText), args.User, args.User);
+
+ // Attempt to paralyze the user after they have taken damage
+ if (_random.Prob(entity.Comp.StunChance))
+ _stun.TryParalyze(args.User, TimeSpan.FromSeconds(entity.Comp.StunSeconds), true);
}
+ // Check if the entity's Throw bool is false, or if the entity has the PullableComponent, then if the entity is currently being pulled.
+ // BeingPulled must be checked because the entity will be spastically thrown around without this.
+ if (!entity.Comp.Throw || !TryComp(entity, out var pullComp) || pullComp.BeingPulled)
+ return;
+
+ _throwingSystem.TryThrow(entity, _random.NextVector2(), entity.Comp.ThrowSpeed, doSpin: true);
}
public void SetIsDamageActiveTo(Entity entity, bool mode)
diff --git a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml
index 20c2488aa7..6f23003c1a 100644
--- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml
+++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml
@@ -171,6 +171,11 @@
fiberMaterial: fibers-leather
fiberColor: fibers-brown
- type: FingerprintMask
+ - type: DamageOnInteractProtection
+ damageProtection:
+ flatReductions:
+ Heat: 10
+ Caustic: 5
- type: entity
parent: ClothingHandsBase
diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml
index ac9757edfb..0c79c233a4 100644
--- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml
+++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml
@@ -250,7 +250,7 @@
name: death nettle
description: This nettle's out for blood.
id: DeathNettle
- parent: ProduceBase
+ parent: [ProduceBase, BaseMajorContraband]
components:
- type: Sprite
sprite: Objects/Specific/Hydroponics/death_nettle.rsi
@@ -261,24 +261,56 @@
- type: MeleeWeapon
damage:
types:
- Heat: 8.5
- Caustic: 8.5
+ Heat: 8
+ Caustic: 8
- type: SolutionContainerManager
solutions:
food:
reagents:
- ReagentId: SulfuricAcid
- Quantity: 3
+ Quantity: 15
- ReagentId: FluorosulfuricAcid
- Quantity: 3
+ Quantity: 15
- type: Produce
seedId: deathNettle
- type: MeleeChemicalInjector
- transferAmount: 2
+ transferAmount: 5
solution: food
- pierceArmor: false
+ pierceArmor: true
- type: Extractable
grindableSolutionName: food
+ - type: DamageOnInteract
+ damage:
+ types:
+ Heat: 4
+ Caustic: 4
+ throw: true
+ throwSpeed: 3
+ interactTimer: 2 # Stop the player from spam clicking the entity
+ ignoreResistances: false
+ popupText: powered-light-component-burn-hand
+ interactSound: /Audio/Effects/lightburn.ogg
+ stunChance: 0.10
+ stunSeconds: 1.5
+ - type: Damageable
+ damageContainer: Inorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 25
+ behaviors:
+ - !type:PlaySoundBehavior
+ sound:
+ path: /Audio/Voice/Diona/diona_salute.ogg
+ params:
+ volume: -5
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - type: DamageOnHit
+ damage:
+ types:
+ Blunt: 5 # The nettle will "wilt" after 5 hits.
- type: entity
name: banana
@@ -1791,10 +1823,10 @@
- type: SolutionContainerManager
solutions:
food:
- maxVol: 30
+ maxVol: 15
reagents:
- ReagentId: Amatoxin
- Quantity: 25
+ Quantity: 10
- ReagentId: Nutriment
Quantity: 5
- type: Sprite
diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml
index ed5f61922a..68524b15ff 100644
--- a/Resources/Prototypes/Hydroponics/seeds.yml
+++ b/Resources/Prototypes/Hydroponics/seeds.yml
@@ -1402,8 +1402,8 @@
chemicals:
Amatoxin:
Min: 1
- Max: 25
- PotencyDivisor: 4
+ Max: 10
+ PotencyDivisor: 12
Nutriment: ## yumby :)
Min: 1
Max: 5