diff --git a/Content.Server/_DV/Vampire/BloodSuckerSystem.cs b/Content.Server/_DV/Vampire/BloodSuckerSystem.cs index e736c631168..d66e07c28d7 100644 --- a/Content.Server/_DV/Vampire/BloodSuckerSystem.cs +++ b/Content.Server/_DV/Vampire/BloodSuckerSystem.cs @@ -1,34 +1,34 @@ using System.Linq; -using Content.Server.Atmos.Components; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Server.Nutrition.Components; -using Content.Shared._DV.Cocoon; -using Content.Shared._DV.Vampire; -using Content.Shared.Body.Systems; -using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Verbs; using Content.Shared.Damage; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Inventory; -using Content.Shared.Popups; -using Content.Shared.Verbs; +using Content.Shared._DV.Vampire; +using Content.Shared._DV.Cocoon; +using Content.Server.Atmos.Components; +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Shared.Chemistry.EntitySystems; +using Content.Server.Popups; +using Content.Server.DoAfter; +using Content.Server.Nutrition.Components; +using Content.Shared.Vampire; using Robust.Shared.Utility; -namespace Content.Server._DV.Vampire +namespace Content.Server.Vampire { public sealed class BloodSuckerSystem : EntitySystem { - [Dependency] private readonly SharedBodySystem _bodySystem = default!; + [Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly PopupSystem _popups = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly StomachSystem _stomachSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - public override void Initialize() { base.Initialize(); @@ -38,270 +38,134 @@ public override void Initialize() private void AddSuckVerb(EntityUid uid, BloodSuckerComponent component, GetVerbsEvent args) { - var (victim, ignoreClothes) = ResolveVictimAndIgnoreClothes(args.Target, component); - if (victim == null || !TryComp(victim.Value, out var bloodstream) || - args.User == victim || !args.CanAccess) - return; - var verb = CreateSuckVerb(uid, victim.Value, component, bloodstream, ignoreClothes); - args.Verbs.Add(verb); + var victim = args.Target; + var ignoreClothes = false; - // Helper method to resolve the victim and ignore clothes flag - (EntityUid? victim, bool ignoreClothes) ResolveVictimAndIgnoreClothes(EntityUid target, - BloodSuckerComponent component) + if (TryComp(args.Target, out var cocoon)) { - if (TryComp(target, out var cocoon)) - { - return (cocoon.Victim ?? target, cocoon.Victim != null); - } - - return component.WebRequired ? (null, false) : (target, false); + victim = cocoon.Victim ?? args.Target; + ignoreClothes = cocoon.Victim != null; } + else if (component.WebRequired) + return; + + if (!TryComp(victim, out var bloodstream) || args.User == victim || !args.CanAccess) + return; - // Local function to encapsulate verb creation - InnateVerb CreateSuckVerb(EntityUid uid, - EntityUid victim, - BloodSuckerComponent component, - BloodstreamComponent bloodstream, - bool ignoreClothes) + InnateVerb verb = new() { - return new InnateVerb + Act = () => { - Act = () => StartSuckDoAfter(uid, victim, component, bloodstream, !ignoreClothes), - Text = Loc.GetString("action-name-suck-blood"), - Icon = new SpriteSpecifier.Texture(new("/Textures/Nyanotrasen/Icons/verbiconfangs.png")), - Priority = 2 - }; - } + StartSuckDoAfter(uid, victim, component, bloodstream, !ignoreClothes); // start doafter + }, + Text = Loc.GetString("action-name-suck-blood"), + Icon = new SpriteSpecifier.Texture(new("/Textures/Nyanotrasen/Icons/verbiconfangs.png")), + Priority = 2, + }; + args.Verbs.Add(verb); } - private const string HeadSlot = "head"; - private const string MaskSlot = "mask"; - private const string BloodName = "Blood"; - public void StartSuckDoAfter(EntityUid bloodSuckerUid, - EntityUid victimUid, - BloodSuckerComponent? bloodSuckerComp = null, - BloodstreamComponent? bloodstreamComp = null, - bool checkConditions = true) + private void OnDoAfter(EntityUid uid, BloodSuckerComponent component, BloodSuckDoAfterEvent args) { - if (!Resolve(bloodSuckerUid, ref bloodSuckerComp) || !Resolve(victimUid, ref bloodstreamComp)) - return; - - if (checkConditions && !PerformPreliminaryChecks(bloodSuckerUid, victimUid)) + if (args.Cancelled || args.Handled || args.Args.Target == null) return; - if (!CheckBloodstreamValidity(victimUid, bloodstreamComp)) - return; - - NotifySuckStart(bloodSuckerUid, victimUid); - - StartDoAfterProcess(bloodSuckerUid, bloodSuckerComp, victimUid); + args.Handled = TrySuck(uid, args.Args.Target.Value); } - private bool PerformPreliminaryChecks(EntityUid bloodSuckerUid, EntityUid victimUid) + public void StartSuckDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodSuckerComponent = null, BloodstreamComponent? stream = null, bool doChecks = true) { - if (!_interactionSystem.InRangeUnobstructed(bloodSuckerUid, victimUid)) - return false; - - if (IsWearingHeadProtection(victimUid) || IsWearingIngestionBlockingMask(bloodSuckerUid)) - return false; - - return true; - } - - private bool IsWearingHeadProtection(EntityUid victimUid) - { - if (_inventorySystem.TryGetSlotEntity(victimUid, HeadSlot, out var helmetUid) && - HasComp(helmetUid)) - { - _popups.PopupEntity( - Loc.GetString("bloodsucker-fail-helmet", ("helmet", helmetUid)), - victimUid, - victimUid, - Shared.Popups.PopupType.Medium); - return true; - } - - return false; - } + if (!Resolve(bloodsucker, ref bloodSuckerComponent) || !Resolve(victim, ref stream)) + return; - private bool IsWearingIngestionBlockingMask(EntityUid bloodSuckerUid) - { - if (_inventorySystem.TryGetSlotEntity(bloodSuckerUid, MaskSlot, out var maskUid) && - EntityManager.TryGetComponent(maskUid, out var blocker) && - blocker.Enabled) + if (doChecks) { - _popups.PopupEntity( - Loc.GetString("bloodsucker-fail-mask", ("mask", maskUid)), - bloodSuckerUid, - bloodSuckerUid, - Shared.Popups.PopupType.Medium); - return true; - } - - return false; - } + if (!_interactionSystem.InRangeUnobstructed(bloodsucker, victim)) + return; - private bool CheckBloodstreamValidity(EntityUid victimUid, BloodstreamComponent bloodstreamComp) - { - if (bloodstreamComp.BloodReagent != BloodName) - { - _popups.PopupEntity(Loc.GetString("bloodsucker-not-blood", ("target", victimUid)), - victimUid, - victimUid, - Shared.Popups.PopupType.Medium); - return false; - } + if (_inventorySystem.TryGetSlotEntity(victim, "head", out var headUid) && HasComp(headUid)) + { + _popups.PopupEntity(Loc.GetString("bloodsucker-fail-helmet", ("helmet", headUid)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + return; + } - if (_solutionSystem.PercentFull(victimUid) != 0) - { - _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victimUid)), - victimUid, - victimUid, - Shared.Popups.PopupType.Medium); - return false; + if (_inventorySystem.TryGetSlotEntity(bloodsucker, "mask", out var maskUid) && + EntityManager.TryGetComponent(maskUid, out var blocker) && + blocker.Enabled) + { + _popups.PopupEntity(Loc.GetString("bloodsucker-fail-mask", ("mask", maskUid)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + return; + } } - return true; - } + if (stream.BloodReagent != "Blood") + _popups.PopupEntity(Loc.GetString("bloodsucker-not-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + else if (_solutionSystem.PercentFull(victim) != 0) + _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + else + _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); - private void NotifySuckStart(EntityUid bloodSuckerUid, EntityUid victimUid) - { - _popups.PopupEntity( - Loc.GetString("bloodsucker-doafter-start", ("target", victimUid)), - victimUid, - bloodSuckerUid, - Shared.Popups.PopupType.Medium); - _popups.PopupEntity( - Loc.GetString("bloodsucker-doafter-start-victim", ("sucker", bloodSuckerUid)), - victimUid, - victimUid, - Shared.Popups.PopupType.LargeCaution); - } + _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution); - private void StartDoAfterProcess(EntityUid bloodSuckerUid, - BloodSuckerComponent bloodSuckerComp, - EntityUid victimUid) - { - var doAfterArgs = new DoAfterArgs(EntityManager, - bloodSuckerUid, - bloodSuckerComp.Delay, - new BloodSuckDoAfterEvent(), - bloodSuckerUid, - victimUid) + var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, new BloodSuckDoAfterEvent(), bloodsucker, target: victim) { BreakOnMove = false, DistanceThreshold = 2f, NeedHand = false }; - _doAfter.TryStartDoAfter(doAfterArgs); - } - private StomachComponent? TryGetStomachComponentFromBloodsucker(EntityUid bloodsucker) - { - var stomachComponent = _bodySystem.GetBodyOrganEntityComps(bloodsucker).FirstOrDefault(); - return stomachComponent; + _doAfter.TryStartDoAfter(args); } - private const float MinBloodLevelPercentage = 0.0f; + public bool TrySuck(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodsuckerComp = null) - private bool TryValidateVictim(EntityUid victim) - { - if (!TryGetValidBloodstream(victim, out var bloodstream)) - return false; - - return IsBloodLevelAboveMinimum(victim, bloodstream); - } - - private bool TryGetValidBloodstream(EntityUid victim, out BloodstreamComponent bloodstream) -{ - bloodstream = null!; - - // Ensure the victim is valid - if (victim == EntityUid.Invalid) - { - Logger.Warning("Attempted to get a BloodstreamComponent from an invalid EntityUid."); - return false; - } - - // Check if blood solution exists - if (bloodstream.BloodSolution == null) - { - Logger.Warning($"BloodstreamComponent on {victim} does not have a valid BloodSolution."); - return false; - } - // Passed all checks - return true; -} - - private bool IsBloodLevelAboveMinimum(EntityUid victim, BloodstreamComponent bloodstream) { - return _bloodstreamSystem.GetBloodLevelPercentage(victim, bloodstream) > MinBloodLevelPercentage; - } + var sharedBloodSuckerSystem = EntitySystem.Get(); - private void OnDoAfter(EntityUid uid, BloodSuckerComponent component, BloodSuckDoAfterEvent args) - { - if (IsDoAfterInvalid(args)) - return; + if (!Resolve(bloodsucker, ref bloodsuckerComp)) + return false; + if (!TryValidateVictim(victim)) + return false; - if (args.Args.Target is EntityUid target) - { - args.Handled = TrySuck(uid, target); - } } + if (!TryGetBloodsuckerStomach(bloodsucker, out var stomach)) + return false; + if (!sharedBloodSuckerSystem.TryValidateSolution(bloodsucker)) + return false; - private static bool IsDoAfterInvalid(BloodSuckDoAfterEvent args) - { - return args.Cancelled || args.Handled || args.Args.Target == null; + sharedBloodSuckerSystem.PlayBloodSuckEffects(bloodsucker, victim); + return CompleteBloodSuck(bloodsucker, victim, stomach, bloodsuckerComp); } - - public bool TrySuck(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodsuckerComp = null) + private bool TryValidateVictim(EntityUid victim) { - var sharedSystem = EntityManager.EntitySysManager.GetEntitySystem(); - - if (!AreValidInputs(bloodsucker, victim, sharedSystem, ref bloodsuckerComp, out var bloodsuckerStomach)) + if (!TryComp(victim, out var bloodstream) || bloodstream.BloodSolution == null) return false; - - sharedSystem.PlayBloodSuckEffects(bloodsucker, victim); - - return CompleteBloodSuck(bloodsucker, victim, bloodsuckerStomach, bloodsuckerComp); + return _bloodstreamSystem.GetBloodLevelPercentage(victim, bloodstream) != 0.0f; } - private bool AreValidInputs(EntityUid bloodsucker, - EntityUid victim, - SharedBloodSuckerSystem sharedSystem, - ref BloodSuckerComponent? bloodsuckerComp, - out StomachComponent bloodsuckerStomach) + private bool TryGetBloodsuckerStomach(EntityUid bloodsucker, out StomachComponent stomach) { - bloodsuckerStomach = default; - - return Resolve(bloodsucker, ref bloodsuckerComp) && - TryValidateVictim(victim) && - StomachComponent(bloodsucker, out bloodsuckerStomach) && - sharedSystem.TryValidateSolution(bloodsucker); + stomach = _bodySystem.GetBodyOrganEntityComps(bloodsucker).FirstOrDefault(); + return true; } - private bool CompleteBloodSuck(EntityUid bloodsucker, - EntityUid victim, - StomachComponent stomach, - BloodSuckerComponent bloodsuckerComp) + + + private bool CompleteBloodSuck(EntityUid bloodsucker, EntityUid victim, StomachComponent stomach, BloodSuckerComponent bloodsuckerComp) { if (!TryComp(victim, out var bloodstream) || bloodstream.BloodSolution == null) return false; - var extractedBloodSolution = - _solutionSystem.SplitSolution(bloodstream.BloodSolution.Value, bloodsuckerComp.UnitsToSuck); - _stomachSystem.TryTransferSolution(bloodsucker, extractedBloodSolution, stomach); + var extractedBlood = _solutionSystem.SplitSolution(bloodstream.BloodSolution.Value, bloodsuckerComp.UnitsToSuck); + _stomachSystem.TryTransferSolution(bloodsucker, extractedBlood, stomach); - ApplyPiercingDamage(victim, 5); - return true; - } + DamageSpecifier damage = new(); + damage.DamageDict.Add("Piercing", 1); + _damageableSystem.TryChangeDamage(victim, damage, true); - private void ApplyPiercingDamage(EntityUid victim, int damageAmount) - { - var damage = new DamageSpecifier { DamageDict = { { "Piercing", damageAmount } } }; - _damageableSystem.TryChangeDamage(victim, damage, true, true); + return true; } } } diff --git a/Content.Shared/_DV/Vampire/BloodSuckDoAfterEvent.cs b/Content.Shared/_DV/Vampire/BloodSuckDoAfterEvent.cs index 109769f21f5..5b88d2ddc99 100644 --- a/Content.Shared/_DV/Vampire/BloodSuckDoAfterEvent.cs +++ b/Content.Shared/_DV/Vampire/BloodSuckDoAfterEvent.cs @@ -1,10 +1,8 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - namespace Content.Shared._DV.Vampire { - [Serializable, NetSerializable] - public sealed partial class BloodSuckDoAfterEvent : SimpleDoAfterEvent + [DataDefinition] + public partial class BloodSuckDoAfterEvent { } } + diff --git a/Content.Shared/_DV/Vampire/BloodSuckerComponent.cs b/Content.Shared/_DV/Vampire/BloodSuckerComponent.cs index 31ba03070ac..d2a28318989 100644 --- a/Content.Shared/_DV/Vampire/BloodSuckerComponent.cs +++ b/Content.Shared/_DV/Vampire/BloodSuckerComponent.cs @@ -1,7 +1,7 @@ namespace Content.Shared._DV.Vampire { [RegisterComponent] - public sealed partial class BloodSuckerComponent : Component + public partial class BloodSuckerComponent { /// /// How much to suck each time we suck. @@ -21,7 +21,7 @@ public sealed partial class BloodSuckerComponent : Component /// Whether to inject chems into a chemstream when we suck something. /// [DataField("injectWhenSuck")] - public bool InjectWhenSuck = false; + public bool InjectWhenSuck; /// /// How many units of our injected chem to inject. @@ -39,6 +39,6 @@ public sealed partial class BloodSuckerComponent : Component /// Whether we need to web the thing up first... /// [DataField("webRequired")] - public bool WebRequired = false; + public bool WebRequired; } } diff --git a/Content.Shared/_DV/Vampire/BloodSuckerSystem.cs b/Content.Shared/_DV/Vampire/BloodSuckerSystem.cs index 9f6cafa2adf..8e32db925d1 100644 --- a/Content.Shared/_DV/Vampire/BloodSuckerSystem.cs +++ b/Content.Shared/_DV/Vampire/BloodSuckerSystem.cs @@ -1,15 +1,17 @@ +using Content.Shared._DV.Vampire; using Content.Shared.Administration.Logs; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; +using Content.Shared.Database; using Content.Shared.HealthExaminable; using Content.Shared.Popups; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; -namespace Content.Shared._DV.Vampire +namespace Content.Shared.Vampire { - public sealed class BloodSuckerSystem : EntitySystem + public sealed class SharedBloodSuckerSystem : EntitySystem { [Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!; [Dependency] private readonly SharedPopupSystem _popups = default!; @@ -27,7 +29,7 @@ public override void Initialize() private void OnHealthExamined(EntityUid uid, BloodSuckedComponent component, HealthBeingExaminedEvent args) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodsucked-health-examine", ("target", uid))); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodsucked-health-examine", ("target", uid))); } private void OnDamageChanged(EntityUid uid, BloodSuckedComponent component, DamageChangedEvent args) @@ -35,30 +37,31 @@ private void OnDamageChanged(EntityUid uid, BloodSuckedComponent component, Dama if (args.DamageIncreased) return; - if (_prototypeManager.TryIndex("Brute", out var brute) && args.Damageable.Damage.TryGetDamageInGroup(brute, out var bruteTotal) - && _prototypeManager.TryIndex("Airloss", out var airloss) && args.Damageable.Damage.TryGetDamageInGroup(airloss, out var airlossTotal)) - if (bruteTotal == 0 && airlossTotal == 0) - RemComp(uid); + if (!_prototypeManager.TryIndex("Brute", out var brute) || + !args.Damageable.Damage.TryGetDamageInGroup(brute, out var bruteTotal) + || !_prototypeManager.TryIndex("Airloss", out var airloss) || + !args.Damageable.Damage.TryGetDamageInGroup(airloss, out var airlossTotal)) + return; + if (bruteTotal == 0 && airlossTotal == 0) + RemComp(uid); } - - private bool TryValidateSolution(EntityUid bloodsucker) + public bool TryValidateSolution(EntityUid bloodsucker) { - if (_solutionSystem.PercentFull(bloodsucker) >= 1) - { - _popups.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), bloodsucker, bloodsucker, Shared.Popups.PopupType.MediumCaution); - return false; - } - return true; + if (!(_solutionSystem.PercentFull(bloodsucker) >= 1)) + return true; + _popups.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), bloodsucker, bloodsucker, PopupType.MediumCaution); + return false; } public void PlayBloodSuckEffects(EntityUid bloodsucker, EntityUid victim) { - _adminLogger.Add(Shared.Database.LogType.MeleeHit, Shared.Database.LogImpact.Medium, $"{ToPrettyString(bloodsucker):player} sucked blood from {ToPrettyString(victim):target}"); + _adminLogger.Add(LogType.MeleeHit, LogImpact.Medium, $"{ToPrettyString(bloodsucker):player} sucked blood from {ToPrettyString(victim):target}"); _audio.PlayPvs("/Audio/Items/drink.ogg", bloodsucker); - _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution); - _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked", ("target", victim)), bloodsucker, bloodsucker, Shared.Popups.PopupType.Medium); + _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked-victim", ("sucker", bloodsucker)), victim, victim, PopupType.LargeCaution); + _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked", ("target", victim)), bloodsucker, bloodsucker, PopupType.Medium); EnsureComp(victim); } + } } diff --git a/Content.Shared/_DV/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs b/Content.Shared/_DV/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs index 3f473de73b0..ac94804639f 100644 --- a/Content.Shared/_DV/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs +++ b/Content.Shared/_DV/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs @@ -4,7 +4,7 @@ namespace Content.Shared._DV.Vampire.Injector /// /// Item that gives a bloodsucker injection glands (for poison, usually) /// - public sealed partial class BloodSuckerGlandInjectorComponent : Component + public partial class BloodSuckerGlandInjectorComponent { public bool Used = false;