Add doafter to filling the hypopen (#40538)
* Initial commit * Small QOL buff * Review changes * Ch-ch-ch-ch-chaaaanges * Review changes * oops * Oh ya fix the fill thing * cleanup warnings make a few more private methods --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
@@ -146,10 +146,10 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
{
|
{
|
||||||
// force open container, if applicable, to avoid confusing people on why it doesn't dispense
|
// force open container, if applicable, to avoid confusing people on why it doesn't dispense
|
||||||
_openable.SetOpen(storedContainer, true);
|
_openable.SetOpen(storedContainer, true);
|
||||||
_solutionTransferSystem.Transfer(reagentDispenser,
|
_solutionTransferSystem.Transfer(new SolutionTransferData(reagentDispenser,
|
||||||
storedContainer, src.Value,
|
storedContainer, src.Value,
|
||||||
outputContainer.Value, dst.Value,
|
outputContainer.Value, dst.Value,
|
||||||
(int)reagentDispenser.Comp.DispenseAmount);
|
(int)reagentDispenser.Comp.DispenseAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateUiState(reagentDispenser);
|
UpdateUiState(reagentDispenser);
|
||||||
|
|||||||
@@ -15,4 +15,10 @@ public sealed partial class DrainableSolutionComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public string Solution = "default";
|
public string Solution = "default";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The drain doafter time required to transfer reagents from the solution.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan DrainTime = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ namespace Content.Shared.Chemistry.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Denotes that there is a solution contained in this entity that can be
|
/// Denotes that there is a solution contained in this entity that can be
|
||||||
/// easily dumped into (that is, completely removed from the dumping container
|
/// easily dumped into (that is, completely removed from the dumping container
|
||||||
/// into this one). Think pouring a container fully into this.
|
/// into this one). Think pouring a container fully into this. The action for this is represented via drag & drop.
|
||||||
|
///
|
||||||
|
/// To represent it being possible to controllably pour volumes into the entity, see <see cref="RefillableSolutionComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class DumpableSolutionComponent : Component
|
public sealed partial class DumpableSolutionComponent : Component
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ public sealed partial class HyposprayComponent : Component
|
|||||||
[DataField]
|
[DataField]
|
||||||
public FixedPoint2 TransferAmount = FixedPoint2.New(5);
|
public FixedPoint2 TransferAmount = FixedPoint2.New(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delay to draw reagents using the hypospray.
|
||||||
|
/// If set, <see cref="RefillableSolutionComponent"/> RefillTime should probably have the same value.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float DrawTime = 0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound that will be played when injecting.
|
/// Sound that will be played when injecting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ namespace Content.Shared.Chemistry.Components;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Denotes that the entity has a solution contained which can be easily added
|
/// Denotes that the entity has a solution contained which can be easily added
|
||||||
/// to. This should go on things that are meant to be refilled, including
|
/// to in controlled volumes. This should go on things that are meant to be refilled, including
|
||||||
/// pouring things into a beaker. If you run it under a sink tap, it's probably
|
/// pouring things into a beaker. The action for this is represented via clicking.
|
||||||
/// refillable.
|
///
|
||||||
|
/// To represent it being possible to just dump entire volumes at once into an entity, see <see cref="DumpableSolutionComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class RefillableSolutionComponent : Component
|
public sealed partial class RefillableSolutionComponent : Component
|
||||||
@@ -23,4 +24,10 @@ public sealed partial class RefillableSolutionComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public FixedPoint2? MaxRefill = null;
|
public FixedPoint2? MaxRefill = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The refill doafter time required to transfer reagents into the solution.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan RefillTime = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||||
using Content.Shared.Chemistry.Hypospray.Events;
|
using Content.Shared.Chemistry.Hypospray.Events;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Forensics;
|
using Content.Shared.Forensics;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
@@ -16,6 +16,7 @@ using Content.Shared.Timing;
|
|||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Chemistry.EntitySystems;
|
namespace Content.Shared.Chemistry.EntitySystems;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -36,6 +38,7 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
|
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
|
||||||
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
|
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
|
||||||
SubscribeLocalEvent<HyposprayComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleModeVerb);
|
SubscribeLocalEvent<HyposprayComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleModeVerb);
|
||||||
|
SubscribeLocalEvent<HyposprayComponent, HyposprayDrawDoAfterEvent>(OnDrawDoAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Ref events
|
#region Ref events
|
||||||
@@ -63,6 +66,20 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
TryDoInject(entity, args.HitEntities[0], args.User);
|
TryDoInject(entity, args.HitEntities[0], args.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDrawDoAfter(Entity<HyposprayComponent> entity, ref HyposprayDrawDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.CanContainerDraw
|
||||||
|
&& args.Target.HasValue
|
||||||
|
&& !EligibleEntity(args.Target.Value, entity)
|
||||||
|
&& _solutionContainers.TryGetDrawableSolution(args.Target.Value, out var drawableSolution, out _))
|
||||||
|
{
|
||||||
|
TryDraw(entity, args.Target.Value, drawableSolution.Value, args.User);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Draw/Inject
|
#region Draw/Inject
|
||||||
@@ -73,7 +90,7 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
&& !EligibleEntity(target, entity)
|
&& !EligibleEntity(target, entity)
|
||||||
&& _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
|
&& _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
|
||||||
{
|
{
|
||||||
return TryDraw(entity, target, drawableSolution.Value, user);
|
return TryStartDraw(entity, target, drawableSolution.Value, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryDoInject(entity, target, user);
|
return TryDoInject(entity, target, user);
|
||||||
@@ -186,17 +203,37 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryDraw(Entity<HyposprayComponent> entity, EntityUid target, Entity<SolutionComponent> targetSolution, EntityUid user)
|
public bool TryStartDraw(Entity<HyposprayComponent> entity, EntityUid target, Entity<SolutionComponent> targetSolution, EntityUid user)
|
||||||
{
|
{
|
||||||
if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln,
|
if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln))
|
||||||
out var solution) || solution.AvailableVolume == 0)
|
return false;
|
||||||
|
|
||||||
|
if (!TryGetDrawAmount(entity, target, targetSolution, user, soln.Value, out _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(EntityManager, user, entity.Comp.DrawTime, new HyposprayDrawDoAfterEvent(), entity, target)
|
||||||
|
{
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnMove = true,
|
||||||
|
NeedHand = true,
|
||||||
|
Hidden = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return _doAfter.TryStartDoAfter(doAfterArgs, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetDrawAmount(Entity<HyposprayComponent> entity, EntityUid target, Entity<SolutionComponent> targetSolution, EntityUid user, Entity<SolutionComponent> solutionEntity, [NotNullWhen(true)] out FixedPoint2? amount)
|
||||||
|
{
|
||||||
|
amount = null;
|
||||||
|
|
||||||
|
if (solutionEntity.Comp.Solution.AvailableVolume == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
|
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
|
||||||
var realTransferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, targetSolution.Comp.Solution.Volume,
|
var realTransferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, targetSolution.Comp.Solution.Volume,
|
||||||
solution.AvailableVolume);
|
solutionEntity.Comp.Solution.AvailableVolume);
|
||||||
|
|
||||||
if (realTransferAmount <= 0)
|
if (realTransferAmount <= 0)
|
||||||
{
|
{
|
||||||
@@ -207,7 +244,19 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var removedSolution = _solutionContainers.Draw(target, targetSolution, realTransferAmount);
|
amount = realTransferAmount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryDraw(Entity<HyposprayComponent> entity, EntityUid target, Entity<SolutionComponent> targetSolution, EntityUid user)
|
||||||
|
{
|
||||||
|
if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!TryGetDrawAmount(entity, target, targetSolution, user, soln.Value, out var amount))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var removedSolution = _solutionContainers.Draw(target, targetSolution, amount.Value);
|
||||||
|
|
||||||
if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
|
if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
|
||||||
{
|
{
|
||||||
@@ -275,3 +324,6 @@ public sealed class HypospraySystem : EntitySystem
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class HyposprayDrawDoAfterEvent : SimpleDoAfterEvent {}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public sealed class ScoopableSolutionSystem : EntitySystem
|
|||||||
!_solution.TryGetRefillableSolution(beaker, out var target, out _))
|
!_solution.TryGetRefillableSolution(beaker, out var target, out _))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var scooped = _solutionTransfer.Transfer(user, ent, src.Value, beaker, target.Value, srcSolution.Volume);
|
var scooped = _solutionTransfer.Transfer(new SolutionTransferData(user, ent, src.Value, beaker, target.Value, srcSolution.Volume));
|
||||||
if (scooped == 0)
|
if (scooped == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Chemistry;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Player;
|
|
||||||
|
|
||||||
namespace Content.Shared.Chemistry.EntitySystems;
|
namespace Content.Shared.Chemistry.EntitySystems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows an entity to transfer solutions with a customizable amount per click.
|
/// Allows an entity to transfer solutions with a customizable amount -per click-.
|
||||||
/// Also provides <see cref="Transfer"/> API for other systems.
|
/// Also provides <see cref="Transfer"/>, <see cref="RefillTransfer"/> and <see cref="DrainTransfer"/> API for other systems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SolutionTransferSystem : EntitySystem
|
public sealed class SolutionTransferSystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -21,6 +21,10 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
|
||||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
|
||||||
|
private EntityQuery<RefillableSolutionComponent> _refillableQuery;
|
||||||
|
private EntityQuery<DrainableSolutionComponent> _drainableQuery;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default transfer amounts for the set-transfer verb.
|
/// Default transfer amounts for the set-transfer verb.
|
||||||
@@ -32,28 +36,18 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SolutionTransferComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
|
SubscribeLocalEvent<SolutionTransferComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
|
||||||
SubscribeLocalEvent<SolutionTransferComponent, AfterInteractEvent>(OnAfterInteract);
|
|
||||||
SubscribeLocalEvent<SolutionTransferComponent, TransferAmountSetValueMessage>(OnTransferAmountSetValueMessage);
|
SubscribeLocalEvent<SolutionTransferComponent, TransferAmountSetValueMessage>(OnTransferAmountSetValueMessage);
|
||||||
}
|
SubscribeLocalEvent<SolutionTransferComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
|
SubscribeLocalEvent<SolutionTransferComponent, SolutionDrainTransferDoAfterEvent>(OnSolutionDrainTransferDoAfter);
|
||||||
|
SubscribeLocalEvent<SolutionTransferComponent, SolutionRefillTransferDoAfterEvent>(OnSolutionFillTransferDoAfter);
|
||||||
|
|
||||||
private void OnTransferAmountSetValueMessage(Entity<SolutionTransferComponent> ent, ref TransferAmountSetValueMessage message)
|
_refillableQuery = GetEntityQuery<RefillableSolutionComponent>();
|
||||||
{
|
_drainableQuery = GetEntityQuery<DrainableSolutionComponent>();
|
||||||
var (uid, comp) = ent;
|
|
||||||
|
|
||||||
var newTransferAmount = FixedPoint2.Clamp(message.Value, comp.MinimumTransferAmount, comp.MaximumTransferAmount);
|
|
||||||
comp.TransferAmount = newTransferAmount;
|
|
||||||
|
|
||||||
if (message.Actor is { Valid: true } user)
|
|
||||||
_popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), uid, user);
|
|
||||||
|
|
||||||
Dirty(uid, comp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddSetTransferVerbs(Entity<SolutionTransferComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
private void AddSetTransferVerbs(Entity<SolutionTransferComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||||
{
|
{
|
||||||
var (uid, comp) = ent;
|
if (!args.CanAccess || !args.CanInteract || !ent.Comp.CanChangeTransferAmount || args.Hands == null)
|
||||||
|
|
||||||
if (!args.CanAccess || !args.CanInteract || !comp.CanChangeTransferAmount || args.Hands == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Custom transfer verb
|
// Custom transfer verb
|
||||||
@@ -66,7 +60,7 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
// TODO: remove server check when bui prediction is a thing
|
// TODO: remove server check when bui prediction is a thing
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_ui.OpenUi(uid, TransferAmountUiKey.Key, @event.User);
|
_ui.OpenUi(ent.Owner, TransferAmountUiKey.Key, @event.User);
|
||||||
},
|
},
|
||||||
Priority = 1
|
Priority = 1
|
||||||
});
|
});
|
||||||
@@ -76,7 +70,7 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
var user = args.User;
|
var user = args.User;
|
||||||
foreach (var amount in DefaultTransferAmounts)
|
foreach (var amount in DefaultTransferAmounts)
|
||||||
{
|
{
|
||||||
if (amount < comp.MinimumTransferAmount || amount > comp.MaximumTransferAmount)
|
if (amount < ent.Comp.MinimumTransferAmount || amount > ent.Comp.MaximumTransferAmount)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AlternativeVerb verb = new();
|
AlternativeVerb verb = new();
|
||||||
@@ -84,11 +78,11 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
verb.Category = VerbCategory.SetTransferAmount;
|
verb.Category = VerbCategory.SetTransferAmount;
|
||||||
verb.Act = () =>
|
verb.Act = () =>
|
||||||
{
|
{
|
||||||
comp.TransferAmount = amount;
|
ent.Comp.TransferAmount = amount;
|
||||||
|
|
||||||
_popup.PopupClient(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, user);
|
_popup.PopupClient(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), ent.Owner, user);
|
||||||
|
|
||||||
Dirty(uid, comp);
|
Dirty(ent.Owner, ent.Comp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// we want to sort by size, not alphabetically by the verb text.
|
// we want to sort by size, not alphabetically by the verb text.
|
||||||
@@ -99,117 +93,301 @@ public sealed class SolutionTransferSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnTransferAmountSetValueMessage(Entity<SolutionTransferComponent> ent, ref TransferAmountSetValueMessage message)
|
||||||
|
{
|
||||||
|
var newTransferAmount = FixedPoint2.Clamp(message.Value, ent.Comp.MinimumTransferAmount, ent.Comp.MaximumTransferAmount);
|
||||||
|
ent.Comp.TransferAmount = newTransferAmount;
|
||||||
|
|
||||||
|
if (message.Actor is { Valid: true } user)
|
||||||
|
_popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), ent.Owner, user);
|
||||||
|
|
||||||
|
Dirty(ent.Owner, ent.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAfterInteract(Entity<SolutionTransferComponent> ent, ref AfterInteractEvent args)
|
private void OnAfterInteract(Entity<SolutionTransferComponent> ent, ref AfterInteractEvent args)
|
||||||
{
|
{
|
||||||
if (!args.CanReach || args.Target is not {} target)
|
if (!args.CanReach || args.Target is not {} target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (uid, comp) = ent;
|
// We have two cases for interaction:
|
||||||
|
// Held Drainable --> Target Refillable
|
||||||
|
// Held Refillable <-- Target Drainable
|
||||||
|
|
||||||
//Special case for reagent tanks, because normally clicking another container will give solution, not take it.
|
// In the case where the target has both Refillable and Drainable, Held --> Target takes priority.
|
||||||
if (comp.CanReceive
|
|
||||||
&& !HasComp<RefillableSolutionComponent>(target) // target must not be refillable (e.g. Reagent Tanks)
|
if (ent.Comp.CanSend
|
||||||
&& _solution.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable
|
&& _drainableQuery.TryComp(ent.Owner, out var heldDrainable)
|
||||||
&& TryComp<RefillableSolutionComponent>(uid, out var refill)
|
&& _refillableQuery.TryComp(target, out var targetRefillable)
|
||||||
&& _solution.TryGetRefillableSolution((uid, refill, null), out var ownerSoln, out var ownerRefill))
|
&& TryGetTransferrableSolutions((ent.Owner, heldDrainable),
|
||||||
|
(target, targetRefillable),
|
||||||
|
out var ownerSoln,
|
||||||
|
out var targetSoln,
|
||||||
|
out _))
|
||||||
{
|
{
|
||||||
var transferAmount = comp.TransferAmount; // This is the player-configurable transfer amount of "uid," not the target reagent tank.
|
args.Handled = true; //If we reach this point, the interaction counts as handled.
|
||||||
|
|
||||||
// if the receiver has a smaller transfer limit, use that instead
|
var transferAmount = ent.Comp.TransferAmount;
|
||||||
if (refill?.MaxRefill is {} maxRefill)
|
if (targetRefillable.MaxRefill is {} maxRefill)
|
||||||
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
|
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
|
||||||
|
|
||||||
var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount);
|
var transferData = new SolutionTransferData(args.User, ent.Owner, ownerSoln.Value, target, targetSoln.Value, transferAmount);
|
||||||
args.Handled = true;
|
var transferTime = targetRefillable.RefillTime + heldDrainable.DrainTime;
|
||||||
if (transferred > 0)
|
|
||||||
|
if (transferTime > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
var toTheBrim = ownerRefill.AvailableVolume == 0;
|
if (!CanTransfer(transferData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, transferTime, new SolutionDrainTransferDoAfterEvent(transferAmount), ent.Owner, target)
|
||||||
|
{
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnMove = true,
|
||||||
|
NeedHand = true,
|
||||||
|
Hidden = true,
|
||||||
|
};
|
||||||
|
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrainTransfer(transferData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent.Comp.CanReceive
|
||||||
|
&& _refillableQuery.TryComp(ent.Owner, out var heldRefillable)
|
||||||
|
&& _drainableQuery.TryComp(target, out var targetDrainable)
|
||||||
|
&& TryGetTransferrableSolutions((target, targetDrainable),
|
||||||
|
(ent.Owner, heldRefillable),
|
||||||
|
out targetSoln,
|
||||||
|
out ownerSoln,
|
||||||
|
out var solution))
|
||||||
|
{
|
||||||
|
args.Handled = true; //If we reach this point, the interaction counts as handled.
|
||||||
|
|
||||||
|
var transferAmount = ent.Comp.TransferAmount; // This is the player-configurable transfer amount of "uid," not the target drainable.
|
||||||
|
if (heldRefillable.MaxRefill is {} maxRefill) // if the receiver has a smaller transfer limit, use that instead
|
||||||
|
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
|
||||||
|
|
||||||
|
var transferData = new SolutionTransferData(args.User, target, targetSoln.Value, ent.Owner, ownerSoln.Value, transferAmount);
|
||||||
|
var transferTime = heldRefillable.RefillTime + targetDrainable.DrainTime;
|
||||||
|
|
||||||
|
if (transferTime > TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
if (!CanTransfer(transferData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, transferTime, new SolutionRefillTransferDoAfterEvent(transferAmount), ent.Owner, target)
|
||||||
|
{
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnMove = true,
|
||||||
|
NeedHand = true,
|
||||||
|
Hidden = true,
|
||||||
|
};
|
||||||
|
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RefillTransfer(transferData, solution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSolutionDrainTransferDoAfter(Entity<SolutionTransferComponent> ent, ref SolutionDrainTransferDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled || args.Target is not { } target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Have to check again, in case something has changed.
|
||||||
|
if (CanSend(ent, target, out var ownerSoln, out var targetSoln))
|
||||||
|
{
|
||||||
|
DrainTransfer(new SolutionTransferData(args.User, ent.Owner, ownerSoln.Value, args.Target.Value, targetSoln.Value, args.Amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSolutionFillTransferDoAfter(Entity<SolutionTransferComponent> ent, ref SolutionRefillTransferDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled || args.Target is not { } target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Have to check again, in case something has changed.
|
||||||
|
if (!CanRecieve(ent, target, out var ownerSoln, out var targetSoln, out var solution))
|
||||||
|
return;
|
||||||
|
|
||||||
|
RefillTransfer(new SolutionTransferData(args.User, target, targetSoln.Value, ent.Owner, ownerSoln.Value, args.Amount), solution);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanSend(Entity<SolutionTransferComponent, DrainableSolutionComponent?> ent,
|
||||||
|
Entity<RefillableSolutionComponent?> target,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? drainable,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? refillable)
|
||||||
|
{
|
||||||
|
drainable = null;
|
||||||
|
refillable = null;
|
||||||
|
|
||||||
|
return ent.Comp1.CanReceive && TryGetTransferrableSolutions(ent.Owner, target, out drainable, out refillable, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanRecieve(Entity<SolutionTransferComponent> ent,
|
||||||
|
EntityUid source,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? drainable,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? refillable,
|
||||||
|
[NotNullWhen(true)] out Solution? solution)
|
||||||
|
{
|
||||||
|
drainable = null;
|
||||||
|
refillable = null;
|
||||||
|
solution = null;
|
||||||
|
|
||||||
|
return ent.Comp.CanReceive && TryGetTransferrableSolutions(source, ent.Owner, out drainable, out refillable, out solution);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetTransferrableSolutions(Entity<DrainableSolutionComponent?> source,
|
||||||
|
Entity<RefillableSolutionComponent?> target,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? drainable,
|
||||||
|
[NotNullWhen(true)] out Entity<SolutionComponent>? refillable,
|
||||||
|
[NotNullWhen(true)] out Solution? solution)
|
||||||
|
{
|
||||||
|
drainable = null;
|
||||||
|
refillable = null;
|
||||||
|
solution = null;
|
||||||
|
|
||||||
|
if (!_drainableQuery.Resolve(source, ref source.Comp) || !_refillableQuery.Resolve(target, ref target.Comp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_solution.TryGetDrainableSolution(source, out drainable, out _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_solution.TryGetRefillableSolution(target, out refillable, out solution))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt to drain a solution into another, such as pouring a bottle into a glass.
|
||||||
|
/// Includes a pop-up if the transfer failed or succeeded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The transfer data making up the transfer.</param>
|
||||||
|
/// <returns>The actual amount transferred.</returns>
|
||||||
|
private void DrainTransfer(SolutionTransferData data)
|
||||||
|
{
|
||||||
|
var transferred = Transfer(data);
|
||||||
|
if (transferred <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", data.TargetEntity));
|
||||||
|
_popup.PopupClient(message, data.SourceEntity, data.User);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt to fill a solution from another container, such as tapping from a water tank.
|
||||||
|
/// Includes a pop-up if the transfer failed or succeeded.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The transfer data making up the transfer.</param>
|
||||||
|
/// <param name="targetSolution">The target solution,included for LoC pop-up purposes.</param>
|
||||||
|
/// <returns>The actual amount transferred.</returns>
|
||||||
|
private void RefillTransfer(SolutionTransferData data, Solution targetSolution)
|
||||||
|
{
|
||||||
|
var transferred = Transfer(data);
|
||||||
|
if (transferred <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var toTheBrim = targetSolution.AvailableVolume == 0;
|
||||||
var msg = toTheBrim
|
var msg = toTheBrim
|
||||||
? "comp-solution-transfer-fill-fully"
|
? "comp-solution-transfer-fill-fully"
|
||||||
: "comp-solution-transfer-fill-normal";
|
: "comp-solution-transfer-fill-normal";
|
||||||
|
|
||||||
_popup.PopupClient(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User);
|
_popup.PopupClient(Loc.GetString(msg, ("owner", data.SourceEntity), ("amount", transferred), ("target", data.TargetEntity)), data.TargetEntity, data.User);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if target is refillable, and owner is drainable
|
|
||||||
if (comp.CanSend
|
|
||||||
&& TryComp<RefillableSolutionComponent>(target, out var targetRefill)
|
|
||||||
&& _solution.TryGetRefillableSolution((target, targetRefill, null), out targetSoln, out _)
|
|
||||||
&& _solution.TryGetDrainableSolution(uid, out ownerSoln, out _))
|
|
||||||
{
|
|
||||||
var transferAmount = comp.TransferAmount;
|
|
||||||
|
|
||||||
if (targetRefill?.MaxRefill is {} maxRefill)
|
|
||||||
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
|
|
||||||
|
|
||||||
var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount);
|
|
||||||
args.Handled = true;
|
|
||||||
if (transferred > 0)
|
|
||||||
{
|
|
||||||
var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target));
|
|
||||||
_popup.PopupClient(message, uid, args.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transfer from a solution to another, allowing either entity to cancel it and show a popup.
|
/// Transfer from a solution to another, allowing either entity to cancel.
|
||||||
|
/// Includes a pop-up if the transfer failed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The actual amount transferred.</returns>
|
/// <returns>The actual amount transferred.</returns>
|
||||||
public FixedPoint2 Transfer(EntityUid user,
|
public FixedPoint2 Transfer(SolutionTransferData data)
|
||||||
EntityUid sourceEntity,
|
|
||||||
Entity<SolutionComponent> source,
|
|
||||||
EntityUid targetEntity,
|
|
||||||
Entity<SolutionComponent> target,
|
|
||||||
FixedPoint2 amount)
|
|
||||||
{
|
{
|
||||||
var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity);
|
var sourceSolution = data.Source.Comp.Solution;
|
||||||
|
var targetSolution = data.Target.Comp.Solution;
|
||||||
|
|
||||||
// Check if the source is cancelling the transfer
|
if (!CanTransfer(data))
|
||||||
RaiseLocalEvent(sourceEntity, ref transferAttempt);
|
|
||||||
if (transferAttempt.CancelReason is {} reason)
|
|
||||||
{
|
|
||||||
_popup.PopupClient(reason, sourceEntity, user);
|
|
||||||
return FixedPoint2.Zero;
|
return FixedPoint2.Zero;
|
||||||
}
|
|
||||||
|
|
||||||
var sourceSolution = source.Comp.Solution;
|
var actualAmount = FixedPoint2.Min(data.Amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume));
|
||||||
if (sourceSolution.Volume == 0)
|
|
||||||
{
|
|
||||||
_popup.PopupClient(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user);
|
|
||||||
return FixedPoint2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the target is cancelling the transfer
|
var solution = _solution.SplitSolution(data.Source, actualAmount);
|
||||||
RaiseLocalEvent(targetEntity, ref transferAttempt);
|
_solution.AddSolution(data.Target, solution);
|
||||||
if (transferAttempt.CancelReason is {} targetReason)
|
|
||||||
{
|
|
||||||
_popup.PopupClient(targetReason, targetEntity, user);
|
|
||||||
return FixedPoint2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetSolution = target.Comp.Solution;
|
var ev = new SolutionTransferredEvent(data.SourceEntity, data.TargetEntity, data.User, actualAmount);
|
||||||
if (targetSolution.AvailableVolume == 0)
|
RaiseLocalEvent(data.TargetEntity, ref ev);
|
||||||
{
|
|
||||||
_popup.PopupClient(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user);
|
|
||||||
return FixedPoint2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume));
|
_adminLogger.Add(LogType.Action,
|
||||||
|
LogImpact.Medium,
|
||||||
var solution = _solution.SplitSolution(source, actualAmount);
|
$"{ToPrettyString(data.User):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(data.TargetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}");
|
||||||
_solution.AddSolution(target, solution);
|
|
||||||
|
|
||||||
var ev = new SolutionTransferredEvent(sourceEntity, targetEntity, user, actualAmount);
|
|
||||||
RaiseLocalEvent(targetEntity, ref ev);
|
|
||||||
|
|
||||||
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
|
||||||
$"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}");
|
|
||||||
|
|
||||||
return actualAmount;
|
return actualAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the source solution can transfer the amount to the target solution, and display a pop-up if it fails.
|
||||||
|
/// </summary>
|
||||||
|
private bool CanTransfer(SolutionTransferData data)
|
||||||
|
{
|
||||||
|
var transferAttempt = new SolutionTransferAttemptEvent(data.SourceEntity, data.TargetEntity);
|
||||||
|
|
||||||
|
// Check if the source is cancelling the transfer
|
||||||
|
RaiseLocalEvent(data.SourceEntity, ref transferAttempt);
|
||||||
|
if (transferAttempt.CancelReason is {} reason)
|
||||||
|
{
|
||||||
|
_popup.PopupClient(reason, data.SourceEntity, data.User);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceSolution = data.Source.Comp.Solution;
|
||||||
|
if (sourceSolution.Volume == 0)
|
||||||
|
{
|
||||||
|
_popup.PopupClient(Loc.GetString("comp-solution-transfer-is-empty", ("target", data.SourceEntity)), data.SourceEntity, data.User);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the target is cancelling the transfer
|
||||||
|
RaiseLocalEvent(data.TargetEntity, ref transferAttempt);
|
||||||
|
if (transferAttempt.CancelReason is {} targetReason)
|
||||||
|
{
|
||||||
|
_popup.PopupClient(targetReason, data.TargetEntity, data.User);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetSolution = data.Target.Comp.Solution;
|
||||||
|
if (targetSolution.AvailableVolume == 0)
|
||||||
|
{
|
||||||
|
_popup.PopupClient(Loc.GetString("comp-solution-transfer-is-full", ("target", data.TargetEntity)), data.TargetEntity, data.User);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of data containing relevant entities and values for transferring reagents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user performing the transfer.</param>
|
||||||
|
/// <param name="sourceEntity">The entity holding the solution container which reagents are being moved from.</param>
|
||||||
|
/// <param name="source">The entity holding the solution from which reagents are being moved away from.</param>
|
||||||
|
/// <param name="targetEntity">The entity holding the solution container which reagents are being moved to.</param>
|
||||||
|
/// <param name="target">The entity holding the solution which reagents are being moved to</param>
|
||||||
|
/// <param name="amount">The amount being moved.</param>
|
||||||
|
public struct SolutionTransferData(EntityUid user, EntityUid sourceEntity, Entity<SolutionComponent> source, EntityUid targetEntity, Entity<SolutionComponent> target, FixedPoint2 amount)
|
||||||
|
{
|
||||||
|
public EntityUid User = user;
|
||||||
|
public EntityUid SourceEntity = sourceEntity;
|
||||||
|
public Entity<SolutionComponent> Source = source;
|
||||||
|
public EntityUid TargetEntity = targetEntity;
|
||||||
|
public Entity<SolutionComponent> Target = target;
|
||||||
|
public FixedPoint2 Amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -234,3 +412,35 @@ public record struct SolutionTransferAttemptEvent(EntityUid From, EntityUid To,
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct SolutionTransferredEvent(EntityUid From, EntityUid To, EntityUid User, FixedPoint2 Amount);
|
public record struct SolutionTransferredEvent(EntityUid From, EntityUid To, EntityUid User, FixedPoint2 Amount);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Doafter event for solution transfers where the held item is drained into the target. Checks for validity both when initiating and when finishing the event.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class SolutionDrainTransferDoAfterEvent : DoAfterEvent
|
||||||
|
{
|
||||||
|
public FixedPoint2 Amount;
|
||||||
|
|
||||||
|
public SolutionDrainTransferDoAfterEvent(FixedPoint2 amount)
|
||||||
|
{
|
||||||
|
Amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DoAfterEvent Clone() => this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Doafter event for solution transfers where the held item is filled from the target. Checks for validity both when initiating and when finishing the event.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class SolutionRefillTransferDoAfterEvent : DoAfterEvent
|
||||||
|
{
|
||||||
|
public FixedPoint2 Amount;
|
||||||
|
|
||||||
|
public SolutionRefillTransferDoAfterEvent(FixedPoint2 amount)
|
||||||
|
{
|
||||||
|
Amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DoAfterEvent Clone() => this;
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public sealed class SolutionDumpingSystem : EntitySystem
|
|||||||
//SubscribeLocalEvent<RefillableSolutionComponent, DragDropDraggedEvent>(OnRefillableDragged); For if you want to refill a container by dragging it into another one. Can't find a use for that currently.
|
//SubscribeLocalEvent<RefillableSolutionComponent, DragDropDraggedEvent>(OnRefillableDragged); For if you want to refill a container by dragging it into another one. Can't find a use for that currently.
|
||||||
SubscribeLocalEvent<DrainableSolutionComponent, DragDropDraggedEvent>(OnDrainableDragged);
|
SubscribeLocalEvent<DrainableSolutionComponent, DragDropDraggedEvent>(OnDrainableDragged);
|
||||||
|
|
||||||
SubscribeLocalEvent<RefillableSolutionComponent, DrainedTargetEvent>(OnDrainedToRefillableDragged);
|
|
||||||
SubscribeLocalEvent<DumpableSolutionComponent, DrainedTargetEvent>(OnDrainedToDumpableDragged);
|
SubscribeLocalEvent<DumpableSolutionComponent, DrainedTargetEvent>(OnDrainedToDumpableDragged);
|
||||||
|
|
||||||
// We use queries for these since CanDropDraggedEvent gets called pretty rapidly
|
// We use queries for these since CanDropDraggedEvent gets called pretty rapidly
|
||||||
@@ -62,7 +61,7 @@ public sealed class SolutionDumpingSystem : EntitySystem
|
|||||||
private void OnDrainableCanDragDropped(Entity<DrainableSolutionComponent> ent, ref CanDropDraggedEvent args)
|
private void OnDrainableCanDragDropped(Entity<DrainableSolutionComponent> ent, ref CanDropDraggedEvent args)
|
||||||
{
|
{
|
||||||
// Easily drawn-from thing can be dragged onto easily refillable thing.
|
// Easily drawn-from thing can be dragged onto easily refillable thing.
|
||||||
if (!_refillableQuery.HasComp(args.Target) && !_dumpQuery.HasComp(args.Target))
|
if (!_dumpQuery.HasComp(args.Target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.CanDrop = true;
|
args.CanDrop = true;
|
||||||
@@ -121,28 +120,6 @@ public sealed class SolutionDumpingSystem : EntitySystem
|
|||||||
_audio.PlayPredicted(AbsorbentComponent.DefaultTransferSound, ent, args.User);
|
_audio.PlayPredicted(AbsorbentComponent.DefaultTransferSound, ent, args.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDrainedToRefillableDragged(Entity<RefillableSolutionComponent> ent, ref DrainedTargetEvent args)
|
|
||||||
{
|
|
||||||
if (!_solContainer.TryGetRefillableSolution((ent, ent.Comp),
|
|
||||||
out var targetSolEnt,
|
|
||||||
out var targetSol))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check openness, hands, source being empty, and target being full.
|
|
||||||
if (!DragInteractionChecks(args.User,
|
|
||||||
args.Source,
|
|
||||||
ent.Owner,
|
|
||||||
args.SourceSolution,
|
|
||||||
targetSol,
|
|
||||||
out var sourceEnt))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_solContainer.TryAddSolution(targetSolEnt.Value,
|
|
||||||
_solContainer.SplitSolution(sourceEnt.Value, targetSol.AvailableVolume));
|
|
||||||
|
|
||||||
_audio.PlayPredicted(AbsorbentComponent.DefaultTransferSound, ent, args.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common checks between dragging handlers.
|
// Common checks between dragging handlers.
|
||||||
private bool DragInteractionChecks(EntityUid user,
|
private bool DragInteractionChecks(EntityUid user,
|
||||||
EntityUid sourceContainer,
|
EntityUid sourceContainer,
|
||||||
|
|||||||
@@ -661,12 +661,15 @@
|
|||||||
maxVol: 10
|
maxVol: 10
|
||||||
- type: RefillableSolution
|
- type: RefillableSolution
|
||||||
solution: hypospray
|
solution: hypospray
|
||||||
|
refillTime: 1.25
|
||||||
|
maxRefill: 5
|
||||||
- type: ExaminableSolution
|
- type: ExaminableSolution
|
||||||
solution: hypospray
|
solution: hypospray
|
||||||
heldOnly: true # Allow examination only when held in hand.
|
heldOnly: true # Allow examination only when held in hand.
|
||||||
exactVolume: true
|
exactVolume: true
|
||||||
- type: Hypospray
|
- type: Hypospray
|
||||||
onlyAffectsMobs: false
|
onlyAffectsMobs: false
|
||||||
|
drawTime: 1.25
|
||||||
- type: UseDelay
|
- type: UseDelay
|
||||||
delay: 0.5
|
delay: 0.5
|
||||||
- type: StaticPrice # A new shitcurity meta
|
- type: StaticPrice # A new shitcurity meta
|
||||||
|
|||||||
Reference in New Issue
Block a user