@@ -18,7 +18,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
|
||||||
SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
SubscribeLocalEvent<DoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<BotanySwabComponent, DoAfterEvent>(OnDoAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,7 +41,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
|
private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
|
||||||
{
|
{
|
||||||
if (args.Target == null || !args.CanReach)
|
if (args.Target == null || !args.CanReach || !HasComp<PlantHolderComponent>(args.Target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
||||||
@@ -57,7 +57,7 @@ public sealed class BotanySwabSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save seed data or cross-pollenate.
|
/// Save seed data or cross-pollenate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDoAfter(DoAfterEvent args)
|
private void OnDoAfter(EntityUid uid, BotanySwabComponent component, DoAfterEvent args)
|
||||||
{
|
{
|
||||||
if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant) || !TryComp<BotanySwabComponent>(args.Args.Used, out var swab))
|
if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant) || !TryComp<BotanySwabComponent>(args.Args.Used, out var swab))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -129,11 +129,18 @@ public sealed partial class ChemistrySystem
|
|||||||
|
|
||||||
private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
|
private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled || args.Cancelled || args.Args.Target == null)
|
if (args.Cancelled)
|
||||||
|
{
|
||||||
|
component.IsInjecting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Handled || args.Args.Target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
|
UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
|
||||||
|
|
||||||
|
component.IsInjecting = false;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,12 +223,18 @@ public sealed partial class ChemistrySystem
|
|||||||
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
|
if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//If it found it's injecting
|
||||||
|
if (component.IsInjecting)
|
||||||
|
return;
|
||||||
|
|
||||||
var actualDelay = MathF.Max(component.Delay, 1f);
|
var actualDelay = MathF.Max(component.Delay, 1f);
|
||||||
|
|
||||||
// Injections take 1 second longer per additional 5u
|
// Injections take 1 second longer per additional 5u
|
||||||
actualDelay += (float) component.TransferAmount / component.Delay - 1;
|
actualDelay += (float) component.TransferAmount / component.Delay - 1;
|
||||||
|
|
||||||
if (user != target)
|
var isTarget = user != target;
|
||||||
|
|
||||||
|
if (isTarget)
|
||||||
{
|
{
|
||||||
// Create a pop-up for the target
|
// Create a pop-up for the target
|
||||||
var userName = Identity.Entity(user, EntityManager);
|
var userName = Identity.Entity(user, EntityManager);
|
||||||
@@ -256,8 +269,12 @@ public sealed partial class ChemistrySystem
|
|||||||
_adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
|
_adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.IsInjecting = true;
|
||||||
|
|
||||||
_doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector)
|
_doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector)
|
||||||
{
|
{
|
||||||
|
RaiseOnTarget = isTarget,
|
||||||
|
RaiseOnUser = !isTarget,
|
||||||
BreakOnUserMove = true,
|
BreakOnUserMove = true,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
BreakOnStun = true,
|
BreakOnStun = true,
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Content.Server.Disease
|
|||||||
// Private Events
|
// Private Events
|
||||||
SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
|
SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
|
||||||
SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
|
SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
|
||||||
SubscribeLocalEvent<DoAfterEvent>(OnSwabDoAfter);
|
SubscribeLocalEvent<DiseaseSwabComponent, DoAfterEvent>(OnSwabDoAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Queue<EntityUid> AddQueue = new();
|
private Queue<EntityUid> AddQueue = new();
|
||||||
@@ -99,7 +99,7 @@ namespace Content.Server.Disease
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args)
|
private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args)
|
||||||
{
|
{
|
||||||
if (args.Target == null || !args.CanReach)
|
if (args.Target == null || !args.CanReach || !HasComp<DiseaseCarrierComponent>(args.Target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (swab.Used)
|
if (swab.Used)
|
||||||
@@ -116,8 +116,12 @@ namespace Content.Server.Disease
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isTarget = args.User != args.Target;
|
||||||
|
|
||||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
_doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
|
||||||
{
|
{
|
||||||
|
RaiseOnTarget = isTarget,
|
||||||
|
RaiseOnUser = !isTarget,
|
||||||
BreakOnTargetMove = true,
|
BreakOnTargetMove = true,
|
||||||
BreakOnUserMove = true,
|
BreakOnUserMove = true,
|
||||||
BreakOnStun = true,
|
BreakOnStun = true,
|
||||||
@@ -301,9 +305,9 @@ namespace Content.Server.Disease
|
|||||||
/// Copies a disease prototype to the swab
|
/// Copies a disease prototype to the swab
|
||||||
/// after the doafter completes.
|
/// after the doafter completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnSwabDoAfter(DoAfterEvent args)
|
private void OnSwabDoAfter(EntityUid uid, DiseaseSwabComponent component, DoAfterEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Target, out var swab))
|
if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Used, out var swab))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
swab.Used = true;
|
swab.Used = true;
|
||||||
@@ -313,7 +317,6 @@ namespace Content.Server.Disease
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
swab.Disease = _random.Pick(carrier.Diseases);
|
swab.Disease = _random.Pick(carrier.Diseases);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ namespace Content.Server.Medical.Components
|
|||||||
[DataField("delay")]
|
[DataField("delay")]
|
||||||
public float Delay = 3f;
|
public float Delay = 3f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancel token to prevent rapid healing
|
||||||
|
/// </summary>
|
||||||
|
[DataField("cancelToken")]
|
||||||
|
public CancellationTokenSource? CancelToken;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delay multiplier when healing yourself.
|
/// Delay multiplier when healing yourself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
@@ -40,6 +41,12 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent<HealingData> args)
|
private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent<HealingData> args)
|
||||||
{
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
{
|
||||||
|
args.AdditionalData.HealingComponent.CancelToken = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null)
|
if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -67,6 +74,7 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
if (args.AdditionalData.HealingComponent.HealingEndSound != null)
|
if (args.AdditionalData.HealingComponent.HealingEndSound != null)
|
||||||
_audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
_audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
||||||
|
|
||||||
|
args.AdditionalData.HealingComponent.CancelToken = null;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +98,7 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
|
|
||||||
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
|
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
|
||||||
{
|
{
|
||||||
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage))
|
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage) || component.CancelToken != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (targetDamage.TotalDamage == 0)
|
if (targetDamage.TotalDamage == 0)
|
||||||
@@ -108,14 +116,21 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
if (component.HealingBeginSound != null)
|
if (component.HealingBeginSound != null)
|
||||||
_audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
_audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
|
||||||
|
|
||||||
var delay = user != target
|
var isNotSelf = user != target;
|
||||||
|
|
||||||
|
var delay = isNotSelf
|
||||||
? component.Delay
|
? component.Delay
|
||||||
: component.Delay * GetScaledHealingPenalty(user, component);
|
: component.Delay * GetScaledHealingPenalty(user, component);
|
||||||
|
|
||||||
|
component.CancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
var healingData = new HealingData(component, stack);
|
var healingData = new HealingData(component, stack);
|
||||||
|
|
||||||
var doAfterEventArgs = new DoAfterEventArgs(user, delay, target: target, used: uid)
|
var doAfterEventArgs = new DoAfterEventArgs(user, delay, cancelToken: component.CancelToken.Token,target: target, used: uid)
|
||||||
{
|
{
|
||||||
|
//Raise the event on the target if it's not self, otherwise raise it on self.
|
||||||
|
RaiseOnTarget = isNotSelf,
|
||||||
|
RaiseOnUser = !isNotSelf,
|
||||||
BreakOnUserMove = true,
|
BreakOnUserMove = true,
|
||||||
BreakOnTargetMove = true,
|
BreakOnTargetMove = true,
|
||||||
// Didn't break on damage as they may be trying to prevent it and
|
// Didn't break on damage as they may be trying to prevent it and
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ namespace Content.Server.Nutrition.Components
|
|||||||
[DataField("burstSound")]
|
[DataField("burstSound")]
|
||||||
public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg");
|
public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this drink being forced on someone else?
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DataField("forceDrink")]
|
||||||
|
public bool ForceDrink;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long it takes to drink this yourself.
|
/// How long it takes to drink this yourself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ namespace Content.Server.Nutrition.Components
|
|||||||
[DataField("eatMessage")]
|
[DataField("eatMessage")]
|
||||||
public string EatMessage = "food-nom";
|
public string EatMessage = "food-nom";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this entity being forcefed?
|
||||||
|
/// Prevents the entity from being forced to eat multiple times if not self
|
||||||
|
/// </summary>
|
||||||
|
[DataField("forceFeed")]
|
||||||
|
public bool ForceFeed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long it takes to eat the food personally.
|
/// How long it takes to eat the food personally.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item)
|
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item)
|
||||||
{
|
{
|
||||||
if (!EntityManager.HasComponent<BodyComponent>(target))
|
if (!EntityManager.HasComponent<BodyComponent>(target) || drink.ForceDrink)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!drink.Opened)
|
if (!drink.Opened)
|
||||||
@@ -241,9 +241,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true))
|
if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var forceDrink = user != target;
|
drink.ForceDrink = user != target;
|
||||||
|
|
||||||
if (forceDrink)
|
if (drink.ForceDrink)
|
||||||
{
|
{
|
||||||
var userName = Identity.Entity(user, EntityManager);
|
var userName = Identity.Entity(user, EntityManager);
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
var drinkData = new DrinkData(drinkSolution, flavors);
|
var drinkData = new DrinkData(drinkSolution, flavors);
|
||||||
|
|
||||||
var doAfterEventArgs = new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay,
|
var doAfterEventArgs = new DoAfterEventArgs(user, drink.ForceDrink ? drink.ForceFeedDelay : drink.Delay,
|
||||||
target: target, used: item)
|
target: target, used: item)
|
||||||
{
|
{
|
||||||
BreakOnUserMove = moveBreak,
|
BreakOnUserMove = moveBreak,
|
||||||
@@ -286,6 +286,14 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent<DrinkData> args)
|
private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent<DrinkData> args)
|
||||||
{
|
{
|
||||||
|
//Special cancel if they're force feeding someone.
|
||||||
|
//Allows self to drink multiple times but prevents force feeding drinks to others rapidly.
|
||||||
|
if (args.Cancelled && component.ForceDrink)
|
||||||
|
{
|
||||||
|
component.ForceDrink = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Handled || args.Cancelled || component.Deleted)
|
if (args.Handled || args.Cancelled || component.Deleted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -295,11 +303,11 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume);
|
var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume);
|
||||||
var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount);
|
var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount);
|
||||||
|
|
||||||
var forceDrink = args.Args.Target.Value != args.Args.User;
|
//var forceDrink = args.Args.Target.Value != args.Args.User;
|
||||||
|
|
||||||
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
|
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
|
||||||
{
|
{
|
||||||
_popupSystem.PopupEntity(forceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
|
_popupSystem.PopupEntity(component.ForceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
|
||||||
|
|
||||||
if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value))
|
if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value))
|
||||||
{
|
{
|
||||||
@@ -320,7 +328,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
{
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value);
|
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value);
|
||||||
|
|
||||||
if (forceDrink)
|
if (component.ForceDrink)
|
||||||
{
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User);
|
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User);
|
||||||
_spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear");
|
_spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear");
|
||||||
@@ -334,7 +342,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
var flavors = args.AdditionalData.FlavorMessage;
|
var flavors = args.AdditionalData.FlavorMessage;
|
||||||
|
|
||||||
if (forceDrink)
|
if (component.ForceDrink)
|
||||||
{
|
{
|
||||||
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
||||||
var userName = Identity.Entity(args.Args.User, EntityManager);
|
var userName = Identity.Entity(args.Args.User, EntityManager);
|
||||||
@@ -366,6 +374,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
//TODO: Grab the stomach UIDs somehow without using Owner
|
//TODO: Grab the stomach UIDs somehow without using Owner
|
||||||
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
|
_stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
|
||||||
|
|
||||||
|
component.ForceDrink = false;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (food == user || EntityManager.TryGetComponent<MobStateComponent>(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs
|
if (food == user || EntityManager.TryGetComponent<MobStateComponent>(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Target can't be fed
|
// Target can't be fed or they're already forcefeeding
|
||||||
if (!EntityManager.HasComponent<BodyComponent>(target))
|
if (!EntityManager.HasComponent<BodyComponent>(target) || foodComp.ForceFeed)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution))
|
if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution))
|
||||||
@@ -111,9 +111,9 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true))
|
if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var forceFeed = user != target;
|
foodComp.ForceFeed = user != target;
|
||||||
|
|
||||||
if (forceFeed)
|
if (foodComp.ForceFeed)
|
||||||
{
|
{
|
||||||
var userName = Identity.Entity(user, EntityManager);
|
var userName = Identity.Entity(user, EntityManager);
|
||||||
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
|
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
|
||||||
@@ -128,16 +128,16 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
_adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
|
_adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var moveBreak = user != target;
|
|
||||||
|
|
||||||
var foodData = new FoodData(foodSolution, flavors, utensils);
|
var foodData = new FoodData(foodSolution, flavors, utensils);
|
||||||
|
|
||||||
var doAfterEventArgs = new DoAfterEventArgs(user, forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food)
|
var doAfterEventArgs = new DoAfterEventArgs(user, foodComp.ForceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food)
|
||||||
{
|
{
|
||||||
BreakOnUserMove = moveBreak,
|
RaiseOnTarget = foodComp.ForceFeed,
|
||||||
|
RaiseOnUser = !foodComp.ForceFeed,
|
||||||
|
BreakOnUserMove = foodComp.ForceFeed,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
BreakOnStun = true,
|
BreakOnStun = true,
|
||||||
BreakOnTargetMove = moveBreak,
|
BreakOnTargetMove = foodComp.ForceFeed,
|
||||||
MovementThreshold = 0.01f,
|
MovementThreshold = 0.01f,
|
||||||
DistanceThreshold = 1.0f,
|
DistanceThreshold = 1.0f,
|
||||||
NeedHand = true
|
NeedHand = true
|
||||||
@@ -151,6 +151,13 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent<FoodData> args)
|
private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent<FoodData> args)
|
||||||
{
|
{
|
||||||
|
//Prevents the target from being force fed food but allows the user to chow down
|
||||||
|
if (args.Cancelled && component.ForceFeed)
|
||||||
|
{
|
||||||
|
component.ForceFeed = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null)
|
if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -166,13 +173,11 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
//TODO: Get the stomach UID somehow without nabbing owner
|
//TODO: Get the stomach UID somehow without nabbing owner
|
||||||
var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split));
|
var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split));
|
||||||
|
|
||||||
var forceFeed = args.Args.Target.Value != args.Args.User;
|
|
||||||
|
|
||||||
// No stomach so just popup a message that they can't eat.
|
// No stomach so just popup a message that they can't eat.
|
||||||
if (firstStomach == null)
|
if (firstStomach == null)
|
||||||
{
|
{
|
||||||
_solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.FoodSolution, split);
|
_solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.FoodSolution, split);
|
||||||
_popupSystem.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
|
_popupSystem.PopupEntity(component.ForceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -182,7 +187,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
var flavors = args.AdditionalData.FlavorMessage;
|
var flavors = args.AdditionalData.FlavorMessage;
|
||||||
|
|
||||||
if (forceFeed)
|
if (component.ForceFeed)
|
||||||
{
|
{
|
||||||
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
|
||||||
var userName = Identity.Entity(args.Args.User, EntityManager);
|
var userName = Identity.Entity(args.Args.User, EntityManager);
|
||||||
|
|||||||
@@ -15,5 +15,8 @@ namespace Content.Server.Tools.Components
|
|||||||
|
|
||||||
[DataField("delay")]
|
[DataField("delay")]
|
||||||
public float Delay = 1f;
|
public float Delay = 1f;
|
||||||
|
|
||||||
|
[DataField("cancelToken")]
|
||||||
|
public CancellationTokenSource? CancelToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,20 @@ public sealed partial class ToolSystem
|
|||||||
{
|
{
|
||||||
SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract);
|
SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract);
|
||||||
SubscribeLocalEvent<TilePryingComponent, TilePryingCompleteEvent>(OnTilePryComplete);
|
SubscribeLocalEvent<TilePryingComponent, TilePryingCompleteEvent>(OnTilePryComplete);
|
||||||
|
SubscribeLocalEvent<TilePryingComponent, TilePryingCancelledEvent>(OnTilePryCancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || !args.CanReach || (args.Target != null && !HasComp<PuddleComponent>(args.Target))) return;
|
||||||
|
|
||||||
|
if (TryPryTile(uid, args.User, component, args.ClickLocation))
|
||||||
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args)
|
private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args)
|
||||||
{
|
{
|
||||||
|
component.CancelToken = null;
|
||||||
var gridUid = args.Coordinates.GetGridUid(EntityManager);
|
var gridUid = args.Coordinates.GetGridUid(EntityManager);
|
||||||
if (!_mapManager.TryGetGrid(gridUid, out var grid))
|
if (!_mapManager.TryGetGrid(gridUid, out var grid))
|
||||||
{
|
{
|
||||||
@@ -34,17 +44,14 @@ public sealed partial class ToolSystem
|
|||||||
_tile.PryTile(tile);
|
_tile.PryTile(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args)
|
private void OnTilePryCancelled(EntityUid uid, TilePryingComponent component, TilePryingCancelledEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled || !args.CanReach || (args.Target != null && !HasComp<PuddleComponent>(args.Target))) return;
|
component.CancelToken = null;
|
||||||
|
|
||||||
if (TryPryTile(uid, args.User, component, args.ClickLocation))
|
|
||||||
args.Handled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
|
private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
|
||||||
{
|
{
|
||||||
if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded)
|
if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded || component.CancelToken != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
|
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
|
||||||
@@ -62,9 +69,11 @@ public sealed partial class ToolSystem
|
|||||||
if (!tileDef.CanCrowbar)
|
if (!tileDef.CanCrowbar)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), targetEntity:toolEntity);
|
component.CancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool))
|
var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), cancelledEv:new TilePryingCancelledEvent() ,targetEntity:toolEntity);
|
||||||
|
|
||||||
|
if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool, cancelToken: component.CancelToken))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
[NetworkedComponent, ComponentProtoName("Injector")]
|
[NetworkedComponent, ComponentProtoName("Injector")]
|
||||||
public abstract class SharedInjectorComponent : Component
|
public abstract class SharedInjectorComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks to see if the entity being injected
|
||||||
|
/// </summary>
|
||||||
|
[DataField("isInjecting")]
|
||||||
|
public bool IsInjecting;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component data used for net updates. Used by client for item status ui
|
/// Component data used for net updates. Used by client for item status ui
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
@@ -31,9 +32,24 @@ public abstract class SharedToolSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, ToolComponent component, DoAfterEvent<ToolEventData> args)
|
private void OnDoAfter(EntityUid uid, ToolComponent component, DoAfterEvent<ToolEventData> args)
|
||||||
{
|
{
|
||||||
if (args.Handled || args.Cancelled || args.AdditionalData.Ev == null)
|
if (args.Handled || args.AdditionalData.Ev == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (args.Cancelled)
|
||||||
|
{
|
||||||
|
if (args.AdditionalData.CancelledEv != null)
|
||||||
|
{
|
||||||
|
if (args.AdditionalData.TargetEntity != null)
|
||||||
|
RaiseLocalEvent(args.AdditionalData.TargetEntity.Value, args.AdditionalData.CancelledEv);
|
||||||
|
else
|
||||||
|
RaiseLocalEvent(args.AdditionalData.CancelledEv);
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ToolFinishUse(uid, args.Args.User, args.AdditionalData.Fuel))
|
if (ToolFinishUse(uid, args.Args.User, args.AdditionalData.Fuel))
|
||||||
{
|
{
|
||||||
if (args.AdditionalData.TargetEntity != null)
|
if (args.AdditionalData.TargetEntity != null)
|
||||||
@@ -43,18 +59,9 @@ public abstract class SharedToolSystem : EntitySystem
|
|||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
else if (args.AdditionalData.CancelledEv != null)
|
|
||||||
{
|
|
||||||
if (args.AdditionalData.TargetEntity != null)
|
|
||||||
RaiseLocalEvent(args.AdditionalData.TargetEntity.Value, args.AdditionalData.CancelledEv);
|
|
||||||
else
|
|
||||||
RaiseLocalEvent(args.AdditionalData.CancelledEv);
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, IEnumerable<string> toolQualitiesNeeded, ToolEventData toolEventData, float fuel = 0f, ToolComponent? toolComponent = null, Func<bool>? doAfterCheck = null)
|
public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, IEnumerable<string> toolQualitiesNeeded, ToolEventData toolEventData, float fuel = 0f, ToolComponent? toolComponent = null, Func<bool>? doAfterCheck = null, CancellationTokenSource? cancelToken = null)
|
||||||
{
|
{
|
||||||
// No logging here, after all that'd mean the caller would need to check if the component is there or not.
|
// No logging here, after all that'd mean the caller would need to check if the component is there or not.
|
||||||
if (!Resolve(tool, ref toolComponent, false))
|
if (!Resolve(tool, ref toolComponent, false))
|
||||||
@@ -70,7 +77,7 @@ public abstract class SharedToolSystem : EntitySystem
|
|||||||
|
|
||||||
if (doAfterDelay > 0f)
|
if (doAfterDelay > 0f)
|
||||||
{
|
{
|
||||||
var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, target:target, used:tool)
|
var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, cancelToken:cancelToken?.Token ?? default, target:target, used:tool)
|
||||||
{
|
{
|
||||||
ExtraCheck = doAfterCheck,
|
ExtraCheck = doAfterCheck,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
|
|||||||
Reference in New Issue
Block a user