/// </summary>
[DataField, AutoNetworkedField]
public bool IsDamageActive = true;
+
+ /// <summary>
+ /// Whether the thing should be thrown from its current position when they interact with the entity
+ /// </summary>
+ [DataField]
+ public bool Throw = false;
+
+ /// <summary>
+ /// The speed applied to the thing when it is thrown
+ /// </summary>
+ [DataField]
+ public int ThrowSpeed = 10;
+
+ /// <summary>
+ /// Time between being able to interact with this entity
+ /// </summary>
+ [DataField]
+ public uint InteractTimer = 0;
+
+ /// <summary>
+ /// Tracks the last time this entity was interacted with, but only if the interaction resulted in the user taking damage
+ /// </summary>
+ [DataField]
+ public TimeSpan LastInteraction = TimeSpan.Zero;
+
+ /// <summary>
+ /// Tracks the time that this entity can be interacted with, but only if the interaction resulted in the user taking damage
+ /// </summary>
+ [DataField]
+ public TimeSpan NextInteraction = TimeSpan.Zero;
+
+ /// <summary>
+ /// Probability that the user will be stunned when they interact with with this entity and took damage
+ /// </summary>
+ [DataField]
+ public float StunChance = 0.0f;
+
+ /// <summary>
+ /// Duration, in seconds, of the stun applied to the user when they interact with the entity and took damage
+ /// </summary>
+ [DataField]
+ public float StunSeconds = 0.0f;
}
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;
[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()
{
/// <param name="args">Contains the user that interacted with the entity</param>
private void OnHandInteract(Entity<DamageOnInteractComponent> 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;
// 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.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<PullableComponent>(entity, out var pullComp) || pullComp.BeingPulled)
+ return;
+
+ _throwingSystem.TryThrow(entity, _random.NextVector2(), entity.Comp.ThrowSpeed, doSpin: true);
}
public void SetIsDamageActiveTo(Entity<DamageOnInteractComponent> entity, bool mode)
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
- 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
- type: SolutionContainerManager
solutions:
food:
- maxVol: 30
+ maxVol: 15
reagents:
- ReagentId: Amatoxin
- Quantity: 25
+ Quantity: 10
- ReagentId: Nutriment
Quantity: 5
- type: Sprite