Skip to content

Commit

Permalink
i have no idea B)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyndomen committed Feb 4, 2025
1 parent 6a9f767 commit e7faa92
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 254 deletions.
318 changes: 91 additions & 227 deletions Content.Server/_DV/Vampire/BloodSuckerSystem.cs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -38,270 +38,134 @@ public override void Initialize()

private void AddSuckVerb(EntityUid uid, BloodSuckerComponent component, GetVerbsEvent<InnateVerb> args)
{
var (victim, ignoreClothes) = ResolveVictimAndIgnoreClothes(args.Target, component);
if (victim == null || !TryComp<BloodstreamComponent>(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<CocoonComponent>(args.Target, out var cocoon))
{
if (TryComp<CocoonComponent>(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<BloodstreamComponent>(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<PressureProtectionComponent>(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<IngestionBlockerComponent>(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<PressureProtectionComponent>(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<IngestionBlockerComponent>(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<StomachComponent>(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<SharedBloodSuckerSystem>();

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<SharedBloodSuckerSystem>();

if (!AreValidInputs(bloodsucker, victim, sharedSystem, ref bloodsuckerComp, out var bloodsuckerStomach))
if (!TryComp<BloodstreamComponent>(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<StomachComponent>(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<BloodstreamComponent>(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;
}
}
}
Loading

0 comments on commit e7faa92

Please sign in to comment.