Death Nettle changes (#25253)

* Added ThornyComponent, ThornyImmuneComponent, and ThornySystem, as well as changed Botanists glove's to have the ThornyImmuneComponent, and for Death Nettle to have the ThornyComponent.

* Added heat damage to the player if they pickup nettle without gloves. Also displays a popup message .

* Revised OnHandPickUp method and reduced whitespace

* Touching death nettle without gloves now does damage split between heat and caustic, and does more damage.

* File-scoped namespace adherence

* Code revisions, and removal of old file.

* Removed thornyImmune key from botanist's gloves in gloves.yml for cleanup / yaml linter

* Adds new generic DamageOnPickup, still very WIP

* Starting on localization, removed _Notes.txt, adds immunity component

* Added OnPickupDamageImmune component to botanists gloves

* Removed botany specific components/system, moved to generic DamageOnPickup. Added code comments. Extra checks in component for whether to toss an item, damage an entity. Still WIP.

* changes to audio and popups

* Removes my system/component/ftl in favor of DamageOnInteract, tweaking values

* me stupid

* Death nettle will 'wilt' after 5 hits

* added interaction delay to stop spam clicking, added a 10% stun (paralyze) chance

* minor changes/cleanup

* more minor changes and cleanup

* Reduced maximum amatoxin within fly amanita spores.

* Readjusted to allow more than 5 amatoxin above 50 potency

* Remove Debug.Log statement from system

* Mark Death Nettle as major contraband.
This commit is contained in:
James
2025-03-29 05:35:16 -04:00
committed by GitHub
parent 8f65144bb8
commit a8e99ff65c
5 changed files with 123 additions and 14 deletions

View File

@@ -44,4 +44,46 @@ public sealed partial class DamageOnInteractComponent : Component
/// </summary> /// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public bool IsDamageActive = true; 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;
} }

View File

@@ -4,9 +4,15 @@ using Content.Shared.Database;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Random;
using Content.Shared.Throwing;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Timing; 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; namespace Content.Shared.Damage.Systems;
@@ -17,6 +23,10 @@ public sealed class DamageOnInteractSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = 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() public override void Initialize()
{ {
@@ -35,6 +45,13 @@ public sealed class DamageOnInteractSystem : EntitySystem
/// <param name="args">Contains the user that interacted with the entity</param> /// <param name="args">Contains the user that interacted with the entity</param>
private void OnHandInteract(Entity<DamageOnInteractComponent> entity, ref InteractHandEvent args) 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) if (!entity.Comp.IsDamageActive)
return; return;
@@ -47,9 +64,8 @@ public sealed class DamageOnInteractSystem : EntitySystem
// or checking the entity for the comp itself if the inventory didn't work // 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)) if (protectiveEntity.Comp == null && TryComp<DamageOnInteractProtectionComponent>(args.User, out var protectiveComp))
{
protectiveEntity = (args.User, protectiveComp); protectiveEntity = (args.User, protectiveComp);
}
// if protectiveComp isn't null after all that, it means the user has protection, // if protectiveComp isn't null after all that, it means the user has protection,
// so let's calculate how much they resist // 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()) 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; 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"); _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); _audioSystem.PlayPredicted(entity.Comp.InteractSound, args.Target, args.User);
if (entity.Comp.PopupText != null) if (entity.Comp.PopupText != null)
_popupSystem.PopupClient(Loc.GetString(entity.Comp.PopupText), args.User, args.User); _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) public void SetIsDamageActiveTo(Entity<DamageOnInteractComponent> entity, bool mode)

View File

@@ -171,6 +171,11 @@
fiberMaterial: fibers-leather fiberMaterial: fibers-leather
fiberColor: fibers-brown fiberColor: fibers-brown
- type: FingerprintMask - type: FingerprintMask
- type: DamageOnInteractProtection
damageProtection:
flatReductions:
Heat: 10
Caustic: 5
- type: entity - type: entity
parent: ClothingHandsBase parent: ClothingHandsBase

View File

@@ -250,7 +250,7 @@
name: death nettle name: death nettle
description: This nettle's out for blood. description: This nettle's out for blood.
id: DeathNettle id: DeathNettle
parent: ProduceBase parent: [ProduceBase, BaseMajorContraband]
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Specific/Hydroponics/death_nettle.rsi sprite: Objects/Specific/Hydroponics/death_nettle.rsi
@@ -261,24 +261,56 @@
- type: MeleeWeapon - type: MeleeWeapon
damage: damage:
types: types:
Heat: 8.5 Heat: 8
Caustic: 8.5 Caustic: 8
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:
reagents: reagents:
- ReagentId: SulfuricAcid - ReagentId: SulfuricAcid
Quantity: 3 Quantity: 15
- ReagentId: FluorosulfuricAcid - ReagentId: FluorosulfuricAcid
Quantity: 3 Quantity: 15
- type: Produce - type: Produce
seedId: deathNettle seedId: deathNettle
- type: MeleeChemicalInjector - type: MeleeChemicalInjector
transferAmount: 2 transferAmount: 5
solution: food solution: food
pierceArmor: false pierceArmor: true
- type: Extractable - type: Extractable
grindableSolutionName: food 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 - type: entity
name: banana name: banana
@@ -1791,10 +1823,10 @@
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:
maxVol: 30 maxVol: 15
reagents: reagents:
- ReagentId: Amatoxin - ReagentId: Amatoxin
Quantity: 25 Quantity: 10
- ReagentId: Nutriment - ReagentId: Nutriment
Quantity: 5 Quantity: 5
- type: Sprite - type: Sprite

View File

@@ -1402,8 +1402,8 @@
chemicals: chemicals:
Amatoxin: Amatoxin:
Min: 1 Min: 1
Max: 25 Max: 10
PotencyDivisor: 4 PotencyDivisor: 12
Nutriment: ## yumby :) Nutriment: ## yumby :)
Min: 1 Min: 1
Max: 5 Max: 5