diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 2b53f34593..41426d7215 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -77,9 +77,15 @@ public sealed class SharpSystem : EntitySystem private void OnDoAfter(EntityUid uid, SharpComponent component, DoAfterEvent args) { - if (args.Handled || args.Cancelled || !TryComp(args.Args.Target, out var butcher)) + if (args.Handled || !TryComp(args.Args.Target, out var butcher)) return; + if (args.Cancelled) + { + component.Butchering.Remove(args.Args.Target.Value); + return; + } + component.Butchering.Remove(args.Args.Target.Value); if (_containerSystem.IsEntityInContainer(args.Args.Target.Value)) diff --git a/Content.Server/Nutrition/Components/DrinkComponent.cs b/Content.Server/Nutrition/Components/DrinkComponent.cs index 14c9712fa8..60784ac412 100644 --- a/Content.Server/Nutrition/Components/DrinkComponent.cs +++ b/Content.Server/Nutrition/Components/DrinkComponent.cs @@ -41,6 +41,12 @@ namespace Content.Server.Nutrition.Components [DataField("forceDrink")] public bool ForceDrink; + /// + /// Is the entity currently drinking or trying to make someone else drink? + /// + [DataField("drinking")] + public bool Drinking; + /// /// How long it takes to drink this yourself. /// diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index c5c12e0fa6..7e9effdfda 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -42,11 +42,16 @@ namespace Content.Server.Nutrition.Components /// /// Is this entity being forcefed? - /// Prevents the entity from being forced to eat multiple times if not self /// [DataField("forceFeed")] public bool ForceFeed; + /// + /// Is this entity eating or being fed? + /// + [DataField(("eating"))] + public bool Eating; + /// /// How long it takes to eat the food personally. /// diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 7cbd2b3023..d919f926ce 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -217,7 +217,7 @@ namespace Content.Server.Nutrition.EntitySystems private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item) { - if (!EntityManager.HasComponent(target) || drink.ForceDrink) + if (!EntityManager.HasComponent(target) || drink.Drinking) return false; if (!drink.Opened) @@ -241,6 +241,7 @@ namespace Content.Server.Nutrition.EntitySystems if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true)) return true; + drink.Drinking = true; drink.ForceDrink = user != target; if (drink.ForceDrink) @@ -286,11 +287,10 @@ namespace Content.Server.Nutrition.EntitySystems /// private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent 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) + if (args.Cancelled) { component.ForceDrink = false; + component.Drinking = false; return; } @@ -374,6 +374,7 @@ namespace Content.Server.Nutrition.EntitySystems //TODO: Grab the stomach UIDs somehow without using Owner _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp); + component.Drinking = false; component.ForceDrink = false; args.Handled = true; } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index bd31a9793c..e0e2a5f0f4 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -86,8 +86,8 @@ namespace Content.Server.Nutrition.EntitySystems if (food == user || EntityManager.TryGetComponent(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs return false; - // Target can't be fed or they're already forcefeeding - if (!EntityManager.HasComponent(target) || foodComp.ForceFeed) + // Target can't be fed or they're already eating + if (!EntityManager.HasComponent(target) || foodComp.Eating) return false; if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution)) @@ -111,6 +111,7 @@ namespace Content.Server.Nutrition.EntitySystems if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true)) return true; + foodComp.Eating = true; foodComp.ForceFeed = user != target; if (foodComp.ForceFeed) @@ -152,8 +153,9 @@ namespace Content.Server.Nutrition.EntitySystems private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent args) { //Prevents the target from being force fed food but allows the user to chow down - if (args.Cancelled && component.ForceFeed) + if (args.Cancelled) { + component.Eating = false; component.ForceFeed = false; return; } @@ -167,6 +169,8 @@ namespace Content.Server.Nutrition.EntitySystems if (!_bodySystem.TryGetBodyOrganComponents(args.Args.Target.Value, out var stomachs, body)) return; + component.Eating = false; + var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, args.AdditionalData.FoodSolution.Volume) : args.AdditionalData.FoodSolution.Volume; var split = _solutionContainerSystem.SplitSolution(uid, args.AdditionalData.FoodSolution, transferAmount); diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index 6013d26601..d9d53686d3 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.Tools; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Interaction; @@ -18,24 +17,12 @@ namespace Content.Server.Repairable public override void Initialize() { SubscribeLocalEvent(Repair); + SubscribeLocalEvent(OnRepairFinished); } - public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args) + private void OnRepairFinished(EntityUid uid, RepairableComponent component, RepairFinishedEvent args) { - // Only try repair the target if it is damaged - if (!EntityManager.TryGetComponent(component.Owner, out DamageableComponent? damageable) || damageable.TotalDamage == 0) - return; - - float delay = component.DoAfterDelay; - - // Add a penalty to how long it takes if the user is repairing itself - if (args.User == args.Target) - delay *= component.SelfRepairPenalty; - - var toolEvData = new ToolEventData(null); - - // Can the tool actually repair this, does it have enough fuel? - if (!_toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, toolEvData, component.FuelCost)) + if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) return; if (component.Damage != null) @@ -43,6 +30,7 @@ namespace Content.Server.Repairable var damageChanged = _damageableSystem.TryChangeDamage(uid, component.Damage, true, false, origin: args.User); _adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(uid):target} by {damageChanged?.Total}"); } + else { // Repair all damage @@ -51,12 +39,43 @@ namespace Content.Server.Repairable } - component.Owner.PopupMessage(args.User, + uid.PopupMessage(args.User, Loc.GetString("comp-repairable-repair", - ("target", component.Owner), + ("target", uid), ("tool", args.Used))); + } + + public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args) + { + // Only try repair the target if it is damaged + if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) + return; + + float delay = component.DoAfterDelay; + + // Add a penalty to how long it takes if the user is repairing itself + if (args.User == args.Target) + delay *= component.SelfRepairPenalty; + + var toolEvData = new ToolEventData(new RepairFinishedEvent(args.User, args.Used), component.FuelCost, targetEntity:uid); + + // Can the tool actually repair this, does it have enough fuel? + if (!_toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, toolEvData, component.FuelCost)) + return; args.Handled = true; } } + + public sealed class RepairFinishedEvent : EntityEventArgs + { + public EntityUid User; + public EntityUid Used; + + public RepairFinishedEvent(EntityUid user, EntityUid used) + { + User = user; + Used = used; + } + } } diff --git a/Content.Server/Storage/EntitySystems/DumpableSystem.cs b/Content.Server/Storage/EntitySystems/DumpableSystem.cs index c0e1c22a1b..a59e439bec 100644 --- a/Content.Server/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Server/Storage/EntitySystems/DumpableSystem.cs @@ -106,6 +106,7 @@ namespace Content.Server.Storage.EntitySystems _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, target: targetUid, used: storageUid) { + RaiseOnTarget = false, BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnStun = true, diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 18c92cc700..3f73e27c02 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -103,6 +103,10 @@ public abstract class SharedDoAfterSystem : EntitySystem foreach (var (_, comp) in EntityManager.EntityQuery()) { + //Don't run the doafter if its comp or owner is deleted. + if (EntityManager.Deleted(comp.Owner) || comp.Deleted) + continue; + foreach (var doAfter in comp.DoAfters.Values.ToArray()) { Run(comp.Owner, comp, doAfter); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 64f0a947db..a2396ce2c7 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -769,6 +769,7 @@ namespace Content.Shared.Interaction return; // all interactions should only happen when in range / unobstructed, so no range check is needed + //TODO: See why this is firing off multiple times var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); RaiseLocalEvent(target, interactUsingEvent, true); DoContactInteraction(user, used, interactUsingEvent); diff --git a/Content.Shared/Radio/Components/EncryptionKeyHolderComponent.cs b/Content.Shared/Radio/Components/EncryptionKeyHolderComponent.cs index ca5d1f647a..c5cf05ea81 100644 --- a/Content.Shared/Radio/Components/EncryptionKeyHolderComponent.cs +++ b/Content.Shared/Radio/Components/EncryptionKeyHolderComponent.cs @@ -42,6 +42,12 @@ public sealed class EncryptionKeyHolderComponent : Component public Container KeyContainer = default!; public const string KeyContainerName = "key_slots"; + /// + /// Blocks multiple attempts to remove the key + /// + [DataField("removing")] + public bool Removing; + /// /// Combined set of radio channels provided by all contained keys. /// diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs index 772255eab8..e3c60b99f9 100644 --- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs +++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs @@ -37,6 +37,28 @@ public sealed class EncryptionKeySystem : EntitySystem SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnContainerModified); SubscribeLocalEvent(OnContainerModified); + SubscribeLocalEvent(OnKeyRemoval); + SubscribeLocalEvent(OnKeyCancelled); + } + + private void OnKeyCancelled(EntityUid uid, EncryptionKeyHolderComponent component, EncryptionRemovalCancelledEvent args) + { + component.Removing = false; + } + + private void OnKeyRemoval(EntityUid uid, EncryptionKeyHolderComponent component, EncryptionRemovalFinishedEvent args) + { + var contained = component.KeyContainer.ContainedEntities.ToArray(); + _container.EmptyContainer(component.KeyContainer, entMan: EntityManager); + foreach (var ent in contained) + { + _hands.PickupOrDrop(args.User, ent); + } + + // if tool use ever gets predicted this needs changing. + _popupSystem.PopupEntity(Loc.GetString("headset-encryption-keys-all-extracted"), uid, args.User); + _audio.PlayPvs(component.KeyExtractionSound, uid); + component.Removing = false; } public void UpdateChannels(EntityUid uid, EncryptionKeyHolderComponent component) @@ -67,7 +89,7 @@ public sealed class EncryptionKeySystem : EntitySystem private void OnInteractUsing(EntityUid uid, EncryptionKeyHolderComponent component, InteractUsingEvent args) { - if (!TryComp(uid, out var storage)) + if (!TryComp(uid, out var storage) || args.Handled || component.Removing) return; if (TryComp(args.Used, out var key)) @@ -99,7 +121,7 @@ public sealed class EncryptionKeySystem : EntitySystem if (!TryComp(args.Used, out var tool) || !tool.Qualities.Contains(component.KeysExtractionMethod)) return; - + args.Handled = true; if (component.KeyContainer.ContainedEntities.Count == 0) @@ -109,21 +131,13 @@ public sealed class EncryptionKeySystem : EntitySystem return; } - var toolEvData = new ToolEventData(null); + //This is honestly the poor mans fix because the InteractUsingEvent fires off 12 times + component.Removing = true; - if(!_toolSystem.UseTool(args.Used, args.User, uid, 0f, new[] { component.KeysExtractionMethod }, toolEvData, toolComponent: tool)) + var toolEvData = new ToolEventData(new EncryptionRemovalFinishedEvent(args.User), cancelledEv: new EncryptionRemovalCancelledEvent(), targetEntity: uid); + + if(!_toolSystem.UseTool(args.Used, args.User, uid, 1f, new[] { component.KeysExtractionMethod }, toolEvData, toolComponent: tool)) return; - - var contained = component.KeyContainer.ContainedEntities.ToArray(); - _container.EmptyContainer(component.KeyContainer, entMan: EntityManager); - foreach (var ent in contained) - { - _hands.PickupOrDrop(args.User, ent); - } - - // if tool use ever gets predicted this needs changing. - _popupSystem.PopupEntity(Loc.GetString("headset-encryption-keys-all-extracted"), uid, args.User); - _audio.PlayPvs(component.KeyExtractionSound, args.Target); } private void OnStartup(EntityUid uid, EncryptionKeyHolderComponent component, ComponentStartup args) @@ -195,4 +209,19 @@ public sealed class EncryptionKeySystem : EntitySystem examineEvent.PushMarkup(msg); } } + + public sealed class EncryptionRemovalFinishedEvent : EntityEventArgs + { + public EntityUid User; + + public EncryptionRemovalFinishedEvent(EntityUid user) + { + User = user; + } + } + + public sealed class EncryptionRemovalCancelledEvent : EntityEventArgs + { + + } }