]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Combine solution injection systems; Fix embeddable injectors (#26268)
authorTayrtahn <tayrtahn@gmail.com>
Mon, 1 Apr 2024 03:39:34 +0000 (23:39 -0400)
committerGitHub <noreply@github.com>
Mon, 1 Apr 2024 03:39:34 +0000 (14:39 +1100)
* Combine injection systems

* Update Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
12 files changed:
Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs [new file with mode: 0644]
Content.Server/Chemistry/Components/MeleeChemicalInjectorComponent.cs
Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs [deleted file]
Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs [new file with mode: 0644]
Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs [new file with mode: 0644]
Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs [deleted file]
Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs [new file with mode: 0644]
Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
Resources/Prototypes/Entities/Objects/Fun/darts.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml

diff --git a/Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs b/Content.Server/Chemistry/Components/BaseSolutionInjectOnEventComponent.cs
new file mode 100644 (file)
index 0000000..708c1ef
--- /dev/null
@@ -0,0 +1,60 @@
+using Content.Shared.FixedPoint;
+using Content.Shared.Inventory;
+
+namespace Content.Server.Chemistry.Components;
+
+/// <summary>
+/// Base class for components that inject a solution into a target's bloodstream in response to an event.
+/// </summary>
+public abstract partial class BaseSolutionInjectOnEventComponent : Component
+{
+    /// <summary>
+    /// How much solution to remove from this entity per target when transferring.
+    /// </summary>
+    /// <remarks>
+    /// Note that this amount is per target, so the total amount removed will be
+    /// multiplied by the number of targets hit.
+    /// </remarks>
+    [DataField]
+    public FixedPoint2 TransferAmount = FixedPoint2.New(1);
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); }
+
+    /// <summary>
+    /// Proportion of the <see cref="TransferAmount"/> that will actually be injected
+    /// into the target's bloodstream. The rest is lost.
+    /// 0 means none of the transferred solution will enter the bloodstream.
+    /// 1 means the entire amount will enter the bloodstream.
+    /// </summary>
+    [DataField("transferEfficiency")]
+    private float _transferEfficiency = 1f;
+
+    /// <summary>
+    /// Solution to inject from.
+    /// </summary>
+    [DataField]
+    public string Solution = "default";
+
+    /// <summary>
+    /// Whether this will inject through hardsuits or not.
+    /// </summary>
+    [DataField]
+    public bool PierceArmor = true;
+
+    /// <summary>
+    /// Contents of popup message to display to the attacker when injection
+    /// fails due to the target wearing a hardsuit.
+    /// </summary>
+    /// <remarks>
+    /// Passed values: $weapon and $target
+    /// </remarks>
+    [DataField]
+    public LocId BlockedByHardsuitPopupMessage = "melee-inject-failed-hardsuit";
+
+    /// <summary>
+    /// If anything covers any of these slots then the injection fails.
+    /// </summary>
+    [DataField]
+    public SlotFlags BlockSlots = SlotFlags.NONE;
+}
index 6b6ce830a96a22e6a06d5d2989fd65672c4c9be1..6b64b82f78740bb38989d901246d7e78edc685ba 100644 (file)
@@ -1,31 +1,8 @@
-using Content.Shared.FixedPoint;
-
-namespace Content.Server.Chemistry.Components
-{
-    [RegisterComponent]
-    public sealed partial class MeleeChemicalInjectorComponent : Component
-    {
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("transferAmount")]
-        public FixedPoint2 TransferAmount { get; set; } = FixedPoint2.New(1);
-
-        [ViewVariables(VVAccess.ReadWrite)]
-        public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); }
-
-        [DataField("transferEfficiency")]
-        private float _transferEfficiency = 1f;
-
-        /// <summary>
-        /// Whether this will inject through hardsuits or not.
-        /// </summary>
-        [DataField("pierceArmor"), ViewVariables(VVAccess.ReadWrite)]
-        public bool PierceArmor = true;
-
-        /// <summary>
-        ///     Solution to inject from.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("solution")]
-        public string Solution { get; set; } = "default";
-    }
-}
+namespace Content.Server.Chemistry.Components;
+
+/// <summary>
+/// Used for melee weapon entities that should try to inject a
+/// contained solution into a target when used to hit it.
+/// </summary>
+[RegisterComponent]
+public sealed partial class MeleeChemicalInjectorComponent : BaseSolutionInjectOnEventComponent { }
diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnCollideComponent.cs
deleted file mode 100644 (file)
index 76bb529..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-using Content.Shared.FixedPoint;
-using Content.Shared.Inventory;
-using Content.Shared.Projectiles;
-
-namespace Content.Server.Chemistry.Components;
-
-/// <summary>
-/// On colliding with an entity that has a bloodstream will dump its solution onto them.
-/// </summary>
-[RegisterComponent]
-public sealed partial class SolutionInjectOnCollideComponent : Component
-{
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("transferAmount")]
-    public FixedPoint2 TransferAmount = FixedPoint2.New(1);
-
-    [ViewVariables(VVAccess.ReadWrite)]
-    public float TransferEfficiency { get => _transferEfficiency; set => _transferEfficiency = Math.Clamp(value, 0, 1); }
-
-    [DataField("transferEfficiency")]
-    private float _transferEfficiency = 1f;
-
-    /// <summary>
-    /// If anything covers any of these slots then the injection fails.
-    /// </summary>
-    [DataField("blockSlots"), ViewVariables(VVAccess.ReadWrite)]
-    public SlotFlags BlockSlots = SlotFlags.MASK;
-}
diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnEmbedComponent.cs
new file mode 100644 (file)
index 0000000..241da38
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Content.Server.Chemistry.Components;
+
+/// <summary>
+/// Used for embeddable entities that should try to inject a
+/// contained solution into a target when they become embedded in it.
+/// </summary>
+[RegisterComponent]
+public sealed partial class SolutionInjectOnEmbedComponent : BaseSolutionInjectOnEventComponent { }
diff --git a/Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs b/Content.Server/Chemistry/Components/SolutionInjectOnProjectileHitComponent.cs
new file mode 100644 (file)
index 0000000..395a075
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Content.Server.Chemistry.Components;
+
+/// <summary>
+/// Used for projectile entities that should try to inject a
+/// contained solution into a target when they hit it.
+/// </summary>
+[RegisterComponent]
+public sealed partial class SolutionInjectOnProjectileHitComponent : BaseSolutionInjectOnEventComponent { }
diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs
deleted file mode 100644 (file)
index fb84aca..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Server.Chemistry.Components;
-using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Shared.Inventory;
-using Content.Shared.Projectiles;
-
-namespace Content.Server.Chemistry.EntitySystems;
-
-public sealed class SolutionInjectOnCollideSystem : EntitySystem
-{
-    [Dependency] private readonly SolutionContainerSystem _solutionContainersSystem = default!;
-    [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
-    [Dependency] private readonly InventorySystem _inventorySystem = default!;
-
-    public override void Initialize()
-    {
-        base.Initialize();
-        SubscribeLocalEvent<SolutionInjectOnCollideComponent, ProjectileHitEvent>(HandleInjection);
-    }
-
-    private void HandleInjection(Entity<SolutionInjectOnCollideComponent> ent, ref ProjectileHitEvent args)
-    {
-        var component = ent.Comp;
-        var target = args.Target;
-
-        if (!TryComp<BloodstreamComponent>(target, out var bloodstream) ||
-            !_solutionContainersSystem.TryGetInjectableSolution(ent.Owner, out var solution, out _))
-        {
-            return;
-        }
-
-        if (component.BlockSlots != 0x0)
-        {
-            var containerEnumerator = _inventorySystem.GetSlotEnumerator(target, component.BlockSlots);
-
-            // TODO add a helper method for this?
-            if (containerEnumerator.MoveNext(out _))
-                return;
-        }
-
-        var solRemoved = _solutionContainersSystem.SplitSolution(solution.Value, component.TransferAmount);
-        var solRemovedVol = solRemoved.Volume;
-
-        var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);
-
-        _bloodstreamSystem.TryAddToChemicals(target, solToInject, bloodstream);
-    }
-}
diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs
new file mode 100644 (file)
index 0000000..8ba36e3
--- /dev/null
@@ -0,0 +1,147 @@
+using Content.Server.Body.Components;
+using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Content.Shared.Inventory;
+using Content.Shared.Popups;
+using Content.Shared.Projectiles;
+using Content.Shared.Tag;
+using Content.Shared.Weapons.Melee.Events;
+
+namespace Content.Server.Chemistry.EntitySystems;
+
+/// <summary>
+/// System for handling the different inheritors of <see cref="BaseSolutionInjectOnEventComponent"/>.
+/// Subscribes to relevent events and performs solution injections when they are raised.
+/// </summary>
+public sealed class SolutionInjectOnCollideSystem : EntitySystem
+{
+    [Dependency] private readonly BloodstreamSystem _bloodstream = default!;
+    [Dependency] private readonly InventorySystem _inventory = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
+    [Dependency] private readonly TagSystem _tag = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<SolutionInjectOnProjectileHitComponent, ProjectileHitEvent>(HandleProjectileHit);
+        SubscribeLocalEvent<SolutionInjectOnEmbedComponent, EmbedEvent>(HandleEmbed);
+        SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(HandleMeleeHit);
+    }
+
+    private void HandleProjectileHit(Entity<SolutionInjectOnProjectileHitComponent> entity, ref ProjectileHitEvent args)
+    {
+        DoInjection((entity.Owner, entity.Comp), args.Target, args.Shooter);
+    }
+
+    private void HandleEmbed(Entity<SolutionInjectOnEmbedComponent> entity, ref EmbedEvent args)
+    {
+        DoInjection((entity.Owner, entity.Comp), args.Embedded, args.Shooter);
+    }
+
+    private void HandleMeleeHit(Entity<MeleeChemicalInjectorComponent> entity, ref MeleeHitEvent args)
+    {
+        // MeleeHitEvent is weird, so we have to filter to make sure we actually
+        // hit something and aren't just examining the weapon.
+        if (args.IsHit)
+            TryInjectTargets((entity.Owner, entity.Comp), args.HitEntities, args.User);
+    }
+
+    private void DoInjection(Entity<BaseSolutionInjectOnEventComponent> injectorEntity, EntityUid target, EntityUid? source = null)
+    {
+        TryInjectTargets(injectorEntity, [target], source);
+    }
+
+    /// <summary>
+    /// Filters <paramref name="targets"/> for valid targets and tries to inject a portion of <see cref="BaseSolutionInjectOnEventComponent.Solution"/> into
+    /// each valid target's bloodstream.
+    /// </summary>
+    /// <remarks>
+    /// Targets are invalid if any of the following are true:
+    /// <list type="bullet">
+    ///     <item>The target does not have a bloodstream.</item>
+    ///     <item><see cref="BaseSolutionInjectOnEventComponent.PierceArmor"/> is false and the target is wearing a hardsuit.</item>
+    ///     <item><see cref="BaseSolutionInjectOnEventComponent.BlockSlots"/> is not NONE and the target has an item equipped in any of the specified slots.</item>
+    /// </list>
+    /// </remarks>
+    /// <returns>true if at least one target was successfully injected, otherwise false</returns>
+    private bool TryInjectTargets(Entity<BaseSolutionInjectOnEventComponent> injector, IReadOnlyList<EntityUid> targets, EntityUid? source = null)
+    {
+        // Make sure we have at least one target
+        if (targets.Count == 0)
+            return false;
+
+        // Get the solution to inject
+        if (!_solutionContainer.TryGetSolution(injector.Owner, injector.Comp.Solution, out var injectorSolution))
+            return false;
+
+        // Build a list of bloodstreams to inject into
+        var targetBloodstreams = new ValueList<Entity<BloodstreamComponent>>();
+        foreach (var target in targets)
+        {
+            if (Deleted(target))
+                continue;
+
+            // Yuck, this is way to hardcodey for my tastes
+            // TODO blocking injection with a hardsuit should probably done with a cancellable event or something
+            if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
+            {
+                // Only show popup to attacker
+                if (source != null)
+                    _popup.PopupEntity(Loc.GetString(injector.Comp.BlockedByHardsuitPopupMessage, ("weapon", injector.Owner), ("target", target)), target, source.Value, PopupType.SmallCaution);
+
+                continue;
+            }
+
+            // Check if the target has anything equipped in a slot that would block injection
+            if (injector.Comp.BlockSlots != SlotFlags.NONE)
+            {
+                var blocked = false;
+                var containerEnumerator = _inventory.GetSlotEnumerator(target, injector.Comp.BlockSlots);
+                while (containerEnumerator.MoveNext(out var container))
+                {
+                    if (container.ContainedEntity != null)
+                    {
+                        blocked = true;
+                        break;
+                    }
+                }
+                if (blocked)
+                    continue;
+            }
+
+            // Make sure the target has a bloodstream
+            if (!TryComp<BloodstreamComponent>(target, out var bloodstream))
+                continue;
+
+
+            // Checks passed; add this target's bloodstream to the list
+            targetBloodstreams.Add((target, bloodstream));
+        }
+
+        // Make sure we got at least one bloodstream
+        if (targetBloodstreams.Count == 0)
+            return false;
+
+        // Extract total needed solution from the injector
+        var removedSolution = _solutionContainer.SplitSolution(injectorSolution.Value, injector.Comp.TransferAmount * targetBloodstreams.Count);
+        // Adjust solution amount based on transfer efficiency
+        var solutionToInject = removedSolution.SplitSolution(removedSolution.Volume * injector.Comp.TransferEfficiency);
+        // Calculate how much of the adjusted solution each target will get
+        var volumePerBloodstream = solutionToInject.Volume * (1f / targetBloodstreams.Count);
+
+        var anySuccess = false;
+        foreach (var targetBloodstream in targetBloodstreams)
+        {
+            // Take our portion of the adjusted solution for this target
+            var individualInjection = solutionToInject.SplitSolution(volumePerBloodstream);
+            // Inject our portion into the target's bloodstream
+            if (_bloodstream.TryAddToChemicals(targetBloodstream.Owner, individualInjection, targetBloodstream.Comp))
+                anySuccess = true;
+        }
+
+        // Huzzah!
+        return anySuccess;
+    }
+}
index ef4b1614770b0579615a5adf283ca96228b692ba..2612e99ec9afa2e4bff3e80f06981b0dc94e2314 100644 (file)
@@ -1,8 +1,4 @@
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
 using Content.Server.Chat.Systems;
-using Content.Server.Chemistry.Components;
-using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.CombatMode.Disarm;
 using Content.Server.Movement.Systems;
 using Content.Shared.Actions.Events;
@@ -14,12 +10,10 @@ using Content.Shared.Database;
 using Content.Shared.Effects;
 using Content.Shared.Hands.Components;
 using Content.Shared.IdentityManagement;
-using Content.Shared.Inventory;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Popups;
 using Content.Shared.Speech.Components;
 using Content.Shared.StatusEffect;
-using Content.Shared.Tag;
 using Content.Shared.Weapons.Melee;
 using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.Audio;
@@ -34,22 +28,17 @@ namespace Content.Server.Weapons.Melee;
 
 public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
 {
-    [Dependency] private readonly SharedAudioSystem            _audio         = default!;
-    [Dependency] private readonly IRobustRandom                _random        = default!;
-    [Dependency] private readonly BloodstreamSystem            _bloodstream   = default!;
-    [Dependency] private readonly ChatSystem                   _chat          = default!;
-    [Dependency] private readonly DamageExamineSystem          _damageExamine = default!;
-    [Dependency] private readonly InventorySystem              _inventory     = default!;
-    [Dependency] private readonly LagCompensationSystem        _lag           = default!;
-    [Dependency] private readonly MobStateSystem               _mobState      = default!;
-    [Dependency] private readonly SharedColorFlashEffectSystem _color         = default!;
-    [Dependency] private readonly SolutionContainerSystem      _solutions     = default!;
-    [Dependency] private readonly TagSystem                    _tag           = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly ChatSystem _chat = default!;
+    [Dependency] private readonly DamageExamineSystem _damageExamine = default!;
+    [Dependency] private readonly LagCompensationSystem _lag = default!;
+    [Dependency] private readonly MobStateSystem _mobState = default!;
+    [Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
 
     public override void Initialize()
     {
         base.Initialize();
-        SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(OnChemicalInjectorHit);
         SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
         SubscribeLocalEvent<MeleeWeaponComponent, DamageExamineEvent>(OnMeleeExamineDamage);
     }
@@ -263,47 +252,4 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
         }
 
     }
-
-    private void OnChemicalInjectorHit(Entity<MeleeChemicalInjectorComponent> entity, ref MeleeHitEvent args)
-    {
-        if (!args.IsHit ||
-            !args.HitEntities.Any() ||
-            !_solutions.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solutionContainer))
-        {
-            return;
-        }
-
-        var hitBloodstreams = new List<(EntityUid Entity, BloodstreamComponent Component)>();
-        var bloodQuery = GetEntityQuery<BloodstreamComponent>();
-
-        foreach (var hit in args.HitEntities)
-        {
-            if (Deleted(hit))
-                continue;
-
-            // prevent deathnettles injecting through hardsuits
-            if (!entity.Comp.PierceArmor && _inventory.TryGetSlotEntity(hit, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
-            {
-                PopupSystem.PopupEntity(Loc.GetString("melee-inject-failed-hardsuit", ("weapon", entity.Owner)), args.User, args.User, PopupType.SmallCaution);
-                continue;
-            }
-
-            if (bloodQuery.TryGetComponent(hit, out var bloodstream))
-                hitBloodstreams.Add((hit, bloodstream));
-        }
-
-        if (!hitBloodstreams.Any())
-            return;
-
-        var removedSolution = _solutions.SplitSolution(solutionContainer.Value, entity.Comp.TransferAmount * hitBloodstreams.Count);
-        var removedVol = removedSolution.Volume;
-        var solutionToInject = removedSolution.SplitSolution(removedVol * entity.Comp.TransferEfficiency);
-        var volPerBloodstream = solutionToInject.Volume * (1 / hitBloodstreams.Count);
-
-        foreach (var (ent, bloodstream) in hitBloodstreams)
-        {
-            var individualInjection = solutionToInject.SplitSolution(volPerBloodstream);
-            _bloodstream.TryAddToChemicals(ent, individualInjection, bloodstream);
-        }
-    }
 }
index 391823dc52f8aca9ea0b3113e532b6339d54911f..4c7ae68420b969602ac7c147030ead888944ff17 100644 (file)
     solution: melee
   - type: InjectableSolution
     solution: melee
-  - type: SolutionInjectOnCollide
+  - type: SolutionInjectOnEmbed
     transferAmount: 2
+    solution: melee
     blockSlots: OUTERCLOTHING
-    fixtureId: "throw-fixture"
   - type: SolutionTransfer
     maxTransferAmount: 2
   - type: Damageable
     solutions:
       melee:
         maxVol: 7
-  - type: SolutionInjectOnCollide
+  - type: SolutionInjectOnEmbed
     transferAmount: 7
-    blockSlots: NONE
-    fixtureId: "throw-fixture"
+    solution: melee
   - type: SolutionTransfer
     maxTransferAmount: 7
 
index 757b8934d433e8b085fa5f3b867bfbf320b152a5..e119a846c9c72e40163f1ff5d5018e08335af640 100644 (file)
@@ -86,8 +86,8 @@
     damage:
       types:
         Piercing: 3
-        Slash: 3 
-        
+        Slash: 3
+
 
 - type: entity
   id: PelletShotgunTranquilizer
     solution: ammo
   - type: DrainableSolution
     solution: ammo
-  - type: SolutionInjectOnCollide
+  - type: SolutionInjectOnProjectileHit
     transferAmount: 15
-    blockSlots: NONE #tranquillizer darts shouldn't be blocked by a mask
+    solution: ammo
   - type: InjectableSolution
     solution: ammo
 
index 8dbcf2b3033a72955c137a9c70c9a482d18ccf4a..52c5dc8a9dbdd2db2fc23df90bda0bce73658981 100644 (file)
@@ -50,9 +50,9 @@
     solution: ammo
   - type: InjectableSolution
     solution: ammo
-  - type: SolutionInjectOnCollide
+  - type: SolutionInjectOnEmbed
     transferAmount: 2
-    blockSlots: NONE
+    solution: ammo
   - type: SolutionTransfer
     maxTransferAmount: 2
   - type: Appearance
index 279fed80433fd291c212cb2ec998d4f66775a284..3758487bd43f7a010141db026168ea209bf955d5 100644 (file)
     solution: melee
   - type: InjectableSolution
     solution: melee
-  - type: SolutionInjectOnCollide
+  - type: SolutionInjectOnEmbed
     transferAmount: 2
-    fixtureId: "throw-fixture"
-    blockSlots: NONE
+    solution: melee
   - type: SolutionTransfer
     maxTransferAmount: 2
   - type: Wieldable