Combine solution injection systems; Fix embeddable injectors (#26268)
* Combine injection systems * Update Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,31 +1,8 @@
|
|||||||
using Content.Shared.FixedPoint;
|
namespace Content.Server.Chemistry.Components;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.Components
|
/// <summary>
|
||||||
{
|
/// Used for melee weapon entities that should try to inject a
|
||||||
[RegisterComponent]
|
/// contained solution into a target when used to hit it.
|
||||||
public sealed partial class MeleeChemicalInjectorComponent : Component
|
/// </summary>
|
||||||
{
|
[RegisterComponent]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
public sealed partial class MeleeChemicalInjectorComponent : BaseSolutionInjectOnEventComponent { }
|
||||||
[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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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 { }
|
||||||
@@ -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 { }
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
using Content.Server.Body.Components;
|
|
||||||
using Content.Server.Body.Systems;
|
|
||||||
using Content.Server.Chat.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.CombatMode.Disarm;
|
||||||
using Content.Server.Movement.Systems;
|
using Content.Server.Movement.Systems;
|
||||||
using Content.Shared.Actions.Events;
|
using Content.Shared.Actions.Events;
|
||||||
@@ -14,12 +10,10 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Effects;
|
using Content.Shared.Effects;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Speech.Components;
|
using Content.Shared.Speech.Components;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Shared.StatusEffect;
|
||||||
using Content.Shared.Tag;
|
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
@@ -34,22 +28,17 @@ namespace Content.Server.Weapons.Melee;
|
|||||||
|
|
||||||
public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = 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!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<MeleeChemicalInjectorComponent, MeleeHitEvent>(OnChemicalInjectorHit);
|
|
||||||
SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
|
SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
|
||||||
SubscribeLocalEvent<MeleeWeaponComponent, DamageExamineEvent>(OnMeleeExamineDamage);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,10 +53,10 @@
|
|||||||
solution: melee
|
solution: melee
|
||||||
- type: InjectableSolution
|
- type: InjectableSolution
|
||||||
solution: melee
|
solution: melee
|
||||||
- type: SolutionInjectOnCollide
|
- type: SolutionInjectOnEmbed
|
||||||
transferAmount: 2
|
transferAmount: 2
|
||||||
|
solution: melee
|
||||||
blockSlots: OUTERCLOTHING
|
blockSlots: OUTERCLOTHING
|
||||||
fixtureId: "throw-fixture"
|
|
||||||
- type: SolutionTransfer
|
- type: SolutionTransfer
|
||||||
maxTransferAmount: 2
|
maxTransferAmount: 2
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
@@ -124,10 +124,9 @@
|
|||||||
solutions:
|
solutions:
|
||||||
melee:
|
melee:
|
||||||
maxVol: 7
|
maxVol: 7
|
||||||
- type: SolutionInjectOnCollide
|
- type: SolutionInjectOnEmbed
|
||||||
transferAmount: 7
|
transferAmount: 7
|
||||||
blockSlots: NONE
|
solution: melee
|
||||||
fixtureId: "throw-fixture"
|
|
||||||
- type: SolutionTransfer
|
- type: SolutionTransfer
|
||||||
maxTransferAmount: 7
|
maxTransferAmount: 7
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@
|
|||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Piercing: 3
|
Piercing: 3
|
||||||
Slash: 3
|
Slash: 3
|
||||||
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: PelletShotgunTranquilizer
|
id: PelletShotgunTranquilizer
|
||||||
@@ -110,9 +110,9 @@
|
|||||||
solution: ammo
|
solution: ammo
|
||||||
- type: DrainableSolution
|
- type: DrainableSolution
|
||||||
solution: ammo
|
solution: ammo
|
||||||
- type: SolutionInjectOnCollide
|
- type: SolutionInjectOnProjectileHit
|
||||||
transferAmount: 15
|
transferAmount: 15
|
||||||
blockSlots: NONE #tranquillizer darts shouldn't be blocked by a mask
|
solution: ammo
|
||||||
- type: InjectableSolution
|
- type: InjectableSolution
|
||||||
solution: ammo
|
solution: ammo
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,9 @@
|
|||||||
solution: ammo
|
solution: ammo
|
||||||
- type: InjectableSolution
|
- type: InjectableSolution
|
||||||
solution: ammo
|
solution: ammo
|
||||||
- type: SolutionInjectOnCollide
|
- type: SolutionInjectOnEmbed
|
||||||
transferAmount: 2
|
transferAmount: 2
|
||||||
blockSlots: NONE
|
solution: ammo
|
||||||
- type: SolutionTransfer
|
- type: SolutionTransfer
|
||||||
maxTransferAmount: 2
|
maxTransferAmount: 2
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
|
|||||||
@@ -65,10 +65,9 @@
|
|||||||
solution: melee
|
solution: melee
|
||||||
- type: InjectableSolution
|
- type: InjectableSolution
|
||||||
solution: melee
|
solution: melee
|
||||||
- type: SolutionInjectOnCollide
|
- type: SolutionInjectOnEmbed
|
||||||
transferAmount: 2
|
transferAmount: 2
|
||||||
fixtureId: "throw-fixture"
|
solution: melee
|
||||||
blockSlots: NONE
|
|
||||||
- type: SolutionTransfer
|
- type: SolutionTransfer
|
||||||
maxTransferAmount: 2
|
maxTransferAmount: 2
|
||||||
- type: Wieldable
|
- type: Wieldable
|
||||||
|
|||||||
Reference in New Issue
Block a user