From 58f9fde75fe923d25aef5c4f36ac1b0f7f87b408 Mon Sep 17 00:00:00 2001 From: Pierson Arnold Date: Sun, 21 Jul 2024 01:09:59 -0500 Subject: [PATCH 1/5] Added Milking/Cumming action with corresponding traits --- .../FloofStation/Traits/HasBoobsComponent.cs | 36 ++++ .../FloofStation/Traits/HasBoobsSystem.cs | 156 ++++++++++++++++++ .../FloofStation/Traits/HasPenisComponent.cs | 36 ++++ .../FloofStation/Traits/HasPenisSystem.cs | 156 ++++++++++++++++++ .../Traits/Events/CummingDoAfterEvent.cs | 9 + .../Traits/Events/MilkingDoAfterEvent.cs | 9 + .../en-US/floofstation/cum/cum-verb.ftl | 5 + .../en-US/floofstation/milk/milk-verb.ftl | 5 + Resources/Locale/en-US/traits/traits.ftl | 9 + .../FloofStation/Reagents/natural_sauce.yml | 4 +- .../Prototypes/FloofStation/Traits/lewd.yml | 49 ++++++ 11 files changed, 472 insertions(+), 2 deletions(-) create mode 100644 Content.Server/FloofStation/Traits/HasBoobsComponent.cs create mode 100644 Content.Server/FloofStation/Traits/HasBoobsSystem.cs create mode 100644 Content.Server/FloofStation/Traits/HasPenisComponent.cs create mode 100644 Content.Server/FloofStation/Traits/HasPenisSystem.cs create mode 100644 Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs create mode 100644 Content.Shared/Floofstation/Traits/Events/MilkingDoAfterEvent.cs create mode 100644 Resources/Locale/en-US/floofstation/cum/cum-verb.ftl create mode 100644 Resources/Locale/en-US/floofstation/milk/milk-verb.ftl create mode 100644 Resources/Prototypes/FloofStation/Traits/lewd.yml diff --git a/Content.Server/FloofStation/Traits/HasBoobsComponent.cs b/Content.Server/FloofStation/Traits/HasBoobsComponent.cs new file mode 100644 index 00000000000..1b98efb6a1b --- /dev/null +++ b/Content.Server/FloofStation/Traits/HasBoobsComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.FloofStation.Traits; + +[RegisterComponent, Access(typeof(HasBoobsSystem))] +public sealed partial class HasBoobsComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly)] + public ProtoId ReagentId = "Milk"; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public string SolutionName = "breasts"; + + [DataField] + public Entity? Solution = null; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public FixedPoint2 QuantityPerUpdate = 25; + + [DataField] + public FixedPoint2 CumMaxVolume = FixedPoint2.New(200); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float HungerUsage = 10f; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1); + + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextGrowth = TimeSpan.FromSeconds(0); +} + diff --git a/Content.Server/FloofStation/Traits/HasBoobsSystem.cs b/Content.Server/FloofStation/Traits/HasBoobsSystem.cs new file mode 100644 index 00000000000..a221bf81d2b --- /dev/null +++ b/Content.Server/FloofStation/Traits/HasBoobsSystem.cs @@ -0,0 +1,156 @@ +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Popups; +using Content.Shared.Chemistry.Components; +using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; +using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Content.Shared.FloofStation.Traits.Events; +using Robust.Shared.Timing; +using JetBrains.Annotations; + +namespace Content.Server.FloofStation.Traits; + +[UsedImplicitly] +public sealed class HasBoobsSystem : EntitySystem +{ + [Dependency] private readonly HungerSystem _hunger = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent>(AddMilkVerb); + SubscribeLocalEvent(OnDoAfter); + } + + //From UdderSystem.cs + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var now = _timing.CurTime; + while (query.MoveNext(out var uid, out var boobs)) + { + if (now < boobs.NextGrowth) + continue; + + boobs.NextGrowth = now + boobs.GrowthDelay; + + if (_mobState.IsDead(uid)) + continue; + + // Actually there is food digestion so no problem with instant reagent generation "OnFeed" + if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + { + // Is there enough nutrition to produce reagent? + if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) + continue; + + _hunger.ModifyHunger(uid, -boobs.HungerUsage, hunger); + } + + if (!_solutionContainer.ResolveSolution(uid, boobs.SolutionName, ref boobs.Solution)) + continue; + + _solutionContainer.TryAddReagent(boobs.Solution.Value, boobs.ReagentId, boobs.QuantityPerUpdate, out _); + } + } + + private void AttemptMilk(Entity penis, EntityUid userUid, EntityUid containerUid) + { + if (!Resolve(penis, ref penis.Comp)) + return; + + var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), penis, penis, used: containerUid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }; + + _doAfterSystem.TryStartDoAfter(doargs); + } + + private void OnDoAfter(Entity entity, ref MilkingDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Used == null) + return; + + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) + return; + + if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) + return; + + args.Handled = true; + var quantity = solution.Volume; + if (quantity == 0) + { + _popupSystem.PopupEntity(Loc.GetString("milk-verb-dry"), entity.Owner, args.Args.User); + return; + } + + if (quantity > targetSolution.AvailableVolume) + quantity = targetSolution.AvailableVolume; + + var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); + _solutionContainer.TryAddSolution(targetSoln.Value, split); + + _popupSystem.PopupEntity(Loc.GetString("milk-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, + args.Args.User, PopupType.Medium); + } + + //Based on BloodstreamSystem.cs + private void OnComponentInit(Entity entity, ref ComponentInit args) + { + var cumSolution = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + + cumSolution.MaxVolume = entity.Comp.CumMaxVolume; + + // Fill breasts solution with MILK + cumSolution.AddReagent(entity.Comp.ReagentId, entity.Comp.CumMaxVolume - cumSolution.Volume); + } + + //Based on UdderSystem.cs + /*TO-DO: + * Check for suit (-loincloth) to prevent action? + * Be able to milk yourself w/o a container. + * -Add a check for a container in the active hand? Yes = fill container. No = Milk go on ground. + * Better text for actions. (Says you/your instead of the person.) + */ + public void AddMilkVerb(Entity entity, ref GetVerbsEvent args) + { + if (args.Using == null || + !args.CanInteract || + !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. + return; + + var cumContainer = entity.Comp.Solution; + var uid = entity.Owner; + var user = args.User; + var used = args.Using.Value; + + AlternativeVerb verb = new() + { + Act = () => + { + AttemptMilk(uid, user, used); + }, + Text = Loc.GetString("milk-verb-get-text"), + Priority = 2 + }; + args.Verbs.Add(verb); + } +} diff --git a/Content.Server/FloofStation/Traits/HasPenisComponent.cs b/Content.Server/FloofStation/Traits/HasPenisComponent.cs new file mode 100644 index 00000000000..7ea6f500eda --- /dev/null +++ b/Content.Server/FloofStation/Traits/HasPenisComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.FloofStation.Traits; + +[RegisterComponent, Access(typeof(HasPenisSystem))] +public sealed partial class HasPenisComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly)] + public ProtoId ReagentId = "Cum"; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public string SolutionName = "penis"; + + [DataField] + public Entity? Solution = null; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public FixedPoint2 QuantityPerUpdate = 25; + + [DataField] + public FixedPoint2 CumMaxVolume = FixedPoint2.New(100); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float HungerUsage = 10f; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1); + + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextGrowth = TimeSpan.FromSeconds(0); +} + diff --git a/Content.Server/FloofStation/Traits/HasPenisSystem.cs b/Content.Server/FloofStation/Traits/HasPenisSystem.cs new file mode 100644 index 00000000000..0fabe17a89b --- /dev/null +++ b/Content.Server/FloofStation/Traits/HasPenisSystem.cs @@ -0,0 +1,156 @@ +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Popups; +using Content.Shared.Chemistry.Components; +using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; +using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Content.Shared.FloofStation.Traits.Events; +using Robust.Shared.Timing; +using JetBrains.Annotations; + +namespace Content.Server.FloofStation.Traits; + +[UsedImplicitly] +public sealed class HasPenisSystem : EntitySystem +{ + [Dependency] private readonly HungerSystem _hunger = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent>(AddCumVerb); + SubscribeLocalEvent(OnDoAfter); + } + + //From UdderSystem.cs + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var now = _timing.CurTime; + while (query.MoveNext(out var uid, out var penis)) + { + if (now < penis.NextGrowth) + continue; + + penis.NextGrowth = now + penis.GrowthDelay; + + if (_mobState.IsDead(uid)) + continue; + + // Actually there is food digestion so no problem with instant reagent generation "OnFeed" + if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + { + // Is there enough nutrition to produce reagent? + if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) + continue; + + _hunger.ModifyHunger(uid, -penis.HungerUsage, hunger); + } + + if (!_solutionContainer.ResolveSolution(uid, penis.SolutionName, ref penis.Solution)) + continue; + + _solutionContainer.TryAddReagent(penis.Solution.Value, penis.ReagentId, penis.QuantityPerUpdate, out _); + } + } + + private void AttemptCum(Entity penis, EntityUid userUid, EntityUid containerUid) + { + if (!Resolve(penis, ref penis.Comp)) + return; + + var doargs = new DoAfterArgs(EntityManager, userUid, 5, new CummingDoAfterEvent(), penis, penis, used: containerUid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }; + + _doAfterSystem.TryStartDoAfter(doargs); + } + + private void OnDoAfter(Entity entity, ref CummingDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Used == null) + return; + + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) + return; + + if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) + return; + + args.Handled = true; + var quantity = solution.Volume; + if (quantity == 0) + { + _popupSystem.PopupEntity(Loc.GetString("cum-verb-dry"), entity.Owner, args.Args.User); + return; + } + + if (quantity > targetSolution.AvailableVolume) + quantity = targetSolution.AvailableVolume; + + var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); + _solutionContainer.TryAddSolution(targetSoln.Value, split); + + _popupSystem.PopupEntity(Loc.GetString("cum-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, + args.Args.User, PopupType.Medium); + } + + //Based on BloodstreamSystem.cs + private void OnComponentInit(Entity entity, ref ComponentInit args) + { + var cumSolution = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + + cumSolution.MaxVolume = entity.Comp.CumMaxVolume; + + // Fill penis solution with CUM + cumSolution.AddReagent(entity.Comp.ReagentId, entity.Comp.CumMaxVolume - cumSolution.Volume); + } + + //Based on UdderSystem.cs + /*TO-DO/Design choices: + * Check for suit (-loincloth) to prevent action? + * Be able to cum on the ground w/o a container. + * -Add a check for a container in the active hand? Yes = fill container. No = Splooge on the ground. + * Better text for actions. (Says you/your instead of the person.) + */ + public void AddCumVerb(Entity entity, ref GetVerbsEvent args) + { + if (args.Using == null || + !args.CanInteract || + !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you cum on the ground? + return; + + var cumContainer = entity.Comp.Solution; + var uid = entity.Owner; + var user = args.User; + var used = args.Using.Value; + + AlternativeVerb verb = new() + { + Act = () => + { + AttemptCum(uid, user, used); + }, + Text = Loc.GetString("cum-verb-get-text"), + Priority = 2 + }; + args.Verbs.Add(verb); + } +} diff --git a/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs b/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs new file mode 100644 index 00000000000..171a904eff3 --- /dev/null +++ b/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.FloofStation.Traits.Events; + +[Serializable, NetSerializable] +public sealed partial class CummingDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Floofstation/Traits/Events/MilkingDoAfterEvent.cs b/Content.Shared/Floofstation/Traits/Events/MilkingDoAfterEvent.cs new file mode 100644 index 00000000000..e4c44b7ad0d --- /dev/null +++ b/Content.Shared/Floofstation/Traits/Events/MilkingDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.FloofStation.Traits.Events; + +[Serializable, NetSerializable] +public sealed partial class MilkingDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Resources/Locale/en-US/floofstation/cum/cum-verb.ftl b/Resources/Locale/en-US/floofstation/cum/cum-verb.ftl new file mode 100644 index 00000000000..8bc69128de2 --- /dev/null +++ b/Resources/Locale/en-US/floofstation/cum/cum-verb.ftl @@ -0,0 +1,5 @@ +cum-verb-dry = Your cum tank is empty. +cum-verb-success = You fill {THE($target)} with {$amount}u of cum from your cock. +cum-verb-success-ground = You pump out some cum all over the ground! + +cum-verb-get-text = Cum diff --git a/Resources/Locale/en-US/floofstation/milk/milk-verb.ftl b/Resources/Locale/en-US/floofstation/milk/milk-verb.ftl new file mode 100644 index 00000000000..e26c0dec0aa --- /dev/null +++ b/Resources/Locale/en-US/floofstation/milk/milk-verb.ftl @@ -0,0 +1,5 @@ +milk-verb-dry = Your breasts are empty. +milk-verb-success = You fill {THE($target)} with {$amount}u of milk from your leaky breasts. +milk-verb-success-ground = You squirt out some milk all over the ground! + +milk-verb-get-text = Milk diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index dc3d407e5e8..eb171c482b2 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -48,3 +48,12 @@ trait-description-NaturalTelepath = As a naturally occuring Telepath, you are ca whether or not you possess any notable psychic powers. This offers all of the same benefits and drawbacks of Latent Psychic, except that you are guaranteed to start with full Telepathy. You may still gain powers as normal for a Latent Psychic. + +trait-name-HasPenis = Cock +trait-description-HasPenis = You have a schlong between your legs. + +trait-name-HasBoobs = Boobs +trait-description-HasBoobs = You have a pair of large mammaries. + +trait-name-HasVagina = Pussy +trait-description-HasVagina = You have a slit between your legs. diff --git a/Resources/Prototypes/FloofStation/Reagents/natural_sauce.yml b/Resources/Prototypes/FloofStation/Reagents/natural_sauce.yml index 86c6984973f..fe089b2ac80 100644 --- a/Resources/Prototypes/FloofStation/Reagents/natural_sauce.yml +++ b/Resources/Prototypes/FloofStation/Reagents/natural_sauce.yml @@ -12,9 +12,9 @@ Drink: effects: - !type:SatiateThirst - factor: 1 + factor: 0.2 - !type:SatiateHunger - factor: 4 + factor: 1 footstepSound: collection: FootstepSticky params: diff --git a/Resources/Prototypes/FloofStation/Traits/lewd.yml b/Resources/Prototypes/FloofStation/Traits/lewd.yml new file mode 100644 index 00000000000..08bfef7cad6 --- /dev/null +++ b/Resources/Prototypes/FloofStation/Traits/lewd.yml @@ -0,0 +1,49 @@ +- type: trait + id: HasPenis + category: Physical + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Borg + - MedicalBorg + components: + - type: HasPenis + - type: SolutionContainerManager + solutions: + penis: + maxVol: 250 + reagents: + - ReagentId: Cum + Quantity: 30 + +- type: trait + id: HasBoobs + category: Physical + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Borg + - MedicalBorg + components: + - type: HasBoobs + - type: SolutionContainerManager + solutions: + breasts: + maxVol: 250 + reagents: + - ReagentId: Milk + Quantity: 30 + +# - type: trait +# id: HasVagina +# category: Physical +# requirements: +# - !type:CharacterJobRequirement +# inverted: true +# jobs: +# - Borg +# - MedicalBorg +# components: +# - type: HasVagina From 513608b3f4966ea2417c4196f8d0fd1f10913793 Mon Sep 17 00:00:00 2001 From: Pierson Arnold Date: Sat, 3 Aug 2024 20:33:27 -0500 Subject: [PATCH 2/5] Cum update: Iteration 2 --- .../CumProducerComponent.cs} | 24 +- .../MilkProducerComponent.cs} | 24 +- .../FloofStation/Traits/HasBoobsSystem.cs | 156 --------- .../FloofStation/Traits/HasPenisSystem.cs | 156 --------- .../FloofStation/Traits/LewdTraitSystem.cs | 326 ++++++++++++++++++ .../Traits/Events/CummingDoAfterEvent.cs | 1 + Resources/Locale/en-US/traits/traits.ftl | 12 +- .../Prototypes/FloofStation/Traits/lewd.yml | 23 +- 8 files changed, 376 insertions(+), 346 deletions(-) rename Content.Server/FloofStation/Traits/{HasPenisComponent.cs => Components/CumProducerComponent.cs} (62%) rename Content.Server/FloofStation/Traits/{HasBoobsComponent.cs => Components/MilkProducerComponent.cs} (62%) delete mode 100644 Content.Server/FloofStation/Traits/HasBoobsSystem.cs delete mode 100644 Content.Server/FloofStation/Traits/HasPenisSystem.cs create mode 100644 Content.Server/FloofStation/Traits/LewdTraitSystem.cs diff --git a/Content.Server/FloofStation/Traits/HasPenisComponent.cs b/Content.Server/FloofStation/Traits/Components/CumProducerComponent.cs similarity index 62% rename from Content.Server/FloofStation/Traits/HasPenisComponent.cs rename to Content.Server/FloofStation/Traits/Components/CumProducerComponent.cs index 7ea6f500eda..aa7ed3fc009 100644 --- a/Content.Server/FloofStation/Traits/HasPenisComponent.cs +++ b/Content.Server/FloofStation/Traits/Components/CumProducerComponent.cs @@ -1,19 +1,25 @@ using Content.Shared.FixedPoint; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; +using Content.Shared.FloofStation.Traits; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.GameStates; namespace Content.Server.FloofStation.Traits; -[RegisterComponent, Access(typeof(HasPenisSystem))] -public sealed partial class HasPenisComponent : Component +[RegisterComponent, Access(typeof(LewdTraitSystem))] +public sealed partial class CumProducerComponent : Component { - [DataField, ViewVariables(VVAccess.ReadOnly)] + [DataField("solutionname"), ViewVariables(VVAccess.ReadWrite)] + public string SolutionName; + + [DataField, ViewVariables(VVAccess.ReadWrite)] public ProtoId ReagentId = "Cum"; - [DataField, ViewVariables(VVAccess.ReadOnly)] - public string SolutionName = "penis"; + [DataField] + public FixedPoint2 MaxVolume = FixedPoint2.New(25); [DataField] public Entity? Solution = null; @@ -22,15 +28,11 @@ public sealed partial class HasPenisComponent : Component public FixedPoint2 QuantityPerUpdate = 25; [DataField] - public FixedPoint2 CumMaxVolume = FixedPoint2.New(100); - - [DataField, ViewVariables(VVAccess.ReadWrite)] public float HungerUsage = 10f; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1); - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextGrowth = TimeSpan.FromSeconds(0); } - diff --git a/Content.Server/FloofStation/Traits/HasBoobsComponent.cs b/Content.Server/FloofStation/Traits/Components/MilkProducerComponent.cs similarity index 62% rename from Content.Server/FloofStation/Traits/HasBoobsComponent.cs rename to Content.Server/FloofStation/Traits/Components/MilkProducerComponent.cs index 1b98efb6a1b..f557394c94c 100644 --- a/Content.Server/FloofStation/Traits/HasBoobsComponent.cs +++ b/Content.Server/FloofStation/Traits/Components/MilkProducerComponent.cs @@ -1,19 +1,25 @@ using Content.Shared.FixedPoint; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; +using Content.Shared.FloofStation.Traits; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.GameStates; namespace Content.Server.FloofStation.Traits; -[RegisterComponent, Access(typeof(HasBoobsSystem))] -public sealed partial class HasBoobsComponent : Component +[RegisterComponent, Access(typeof(LewdTraitSystem))] +public sealed partial class MilkProducerComponent : Component { - [DataField, ViewVariables(VVAccess.ReadOnly)] + [DataField("solutionname"), ViewVariables(VVAccess.ReadWrite)] + public string SolutionName; + + [DataField, ViewVariables(VVAccess.ReadWrite)] public ProtoId ReagentId = "Milk"; - [DataField, ViewVariables(VVAccess.ReadOnly)] - public string SolutionName = "breasts"; + [DataField] + public FixedPoint2 MaxVolume = FixedPoint2.New(25); [DataField] public Entity? Solution = null; @@ -22,15 +28,11 @@ public sealed partial class HasBoobsComponent : Component public FixedPoint2 QuantityPerUpdate = 25; [DataField] - public FixedPoint2 CumMaxVolume = FixedPoint2.New(200); - - [DataField, ViewVariables(VVAccess.ReadWrite)] public float HungerUsage = 10f; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1); - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextGrowth = TimeSpan.FromSeconds(0); } - diff --git a/Content.Server/FloofStation/Traits/HasBoobsSystem.cs b/Content.Server/FloofStation/Traits/HasBoobsSystem.cs deleted file mode 100644 index a221bf81d2b..00000000000 --- a/Content.Server/FloofStation/Traits/HasBoobsSystem.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Popups; -using Content.Shared.Chemistry.Components; -using Content.Shared.DoAfter; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Popups; -using Content.Shared.Verbs; -using Content.Shared.FloofStation.Traits.Events; -using Robust.Shared.Timing; -using JetBrains.Annotations; - -namespace Content.Server.FloofStation.Traits; - -[UsedImplicitly] -public sealed class HasBoobsSystem : EntitySystem -{ - [Dependency] private readonly HungerSystem _hunger = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent>(AddMilkVerb); - SubscribeLocalEvent(OnDoAfter); - } - - //From UdderSystem.cs - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - var now = _timing.CurTime; - while (query.MoveNext(out var uid, out var boobs)) - { - if (now < boobs.NextGrowth) - continue; - - boobs.NextGrowth = now + boobs.GrowthDelay; - - if (_mobState.IsDead(uid)) - continue; - - // Actually there is food digestion so no problem with instant reagent generation "OnFeed" - if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) - { - // Is there enough nutrition to produce reagent? - if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) - continue; - - _hunger.ModifyHunger(uid, -boobs.HungerUsage, hunger); - } - - if (!_solutionContainer.ResolveSolution(uid, boobs.SolutionName, ref boobs.Solution)) - continue; - - _solutionContainer.TryAddReagent(boobs.Solution.Value, boobs.ReagentId, boobs.QuantityPerUpdate, out _); - } - } - - private void AttemptMilk(Entity penis, EntityUid userUid, EntityUid containerUid) - { - if (!Resolve(penis, ref penis.Comp)) - return; - - var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), penis, penis, used: containerUid) - { - BreakOnUserMove = true, - BreakOnDamage = true, - BreakOnTargetMove = true, - MovementThreshold = 1.0f, - }; - - _doAfterSystem.TryStartDoAfter(doargs); - } - - private void OnDoAfter(Entity entity, ref MilkingDoAfterEvent args) - { - if (args.Cancelled || args.Handled || args.Args.Used == null) - return; - - if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) - return; - - if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) - return; - - args.Handled = true; - var quantity = solution.Volume; - if (quantity == 0) - { - _popupSystem.PopupEntity(Loc.GetString("milk-verb-dry"), entity.Owner, args.Args.User); - return; - } - - if (quantity > targetSolution.AvailableVolume) - quantity = targetSolution.AvailableVolume; - - var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); - _solutionContainer.TryAddSolution(targetSoln.Value, split); - - _popupSystem.PopupEntity(Loc.GetString("milk-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, - args.Args.User, PopupType.Medium); - } - - //Based on BloodstreamSystem.cs - private void OnComponentInit(Entity entity, ref ComponentInit args) - { - var cumSolution = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); - - cumSolution.MaxVolume = entity.Comp.CumMaxVolume; - - // Fill breasts solution with MILK - cumSolution.AddReagent(entity.Comp.ReagentId, entity.Comp.CumMaxVolume - cumSolution.Volume); - } - - //Based on UdderSystem.cs - /*TO-DO: - * Check for suit (-loincloth) to prevent action? - * Be able to milk yourself w/o a container. - * -Add a check for a container in the active hand? Yes = fill container. No = Milk go on ground. - * Better text for actions. (Says you/your instead of the person.) - */ - public void AddMilkVerb(Entity entity, ref GetVerbsEvent args) - { - if (args.Using == null || - !args.CanInteract || - !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. - return; - - var cumContainer = entity.Comp.Solution; - var uid = entity.Owner; - var user = args.User; - var used = args.Using.Value; - - AlternativeVerb verb = new() - { - Act = () => - { - AttemptMilk(uid, user, used); - }, - Text = Loc.GetString("milk-verb-get-text"), - Priority = 2 - }; - args.Verbs.Add(verb); - } -} diff --git a/Content.Server/FloofStation/Traits/HasPenisSystem.cs b/Content.Server/FloofStation/Traits/HasPenisSystem.cs deleted file mode 100644 index 0fabe17a89b..00000000000 --- a/Content.Server/FloofStation/Traits/HasPenisSystem.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Popups; -using Content.Shared.Chemistry.Components; -using Content.Shared.DoAfter; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Popups; -using Content.Shared.Verbs; -using Content.Shared.FloofStation.Traits.Events; -using Robust.Shared.Timing; -using JetBrains.Annotations; - -namespace Content.Server.FloofStation.Traits; - -[UsedImplicitly] -public sealed class HasPenisSystem : EntitySystem -{ - [Dependency] private readonly HungerSystem _hunger = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent>(AddCumVerb); - SubscribeLocalEvent(OnDoAfter); - } - - //From UdderSystem.cs - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - var now = _timing.CurTime; - while (query.MoveNext(out var uid, out var penis)) - { - if (now < penis.NextGrowth) - continue; - - penis.NextGrowth = now + penis.GrowthDelay; - - if (_mobState.IsDead(uid)) - continue; - - // Actually there is food digestion so no problem with instant reagent generation "OnFeed" - if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) - { - // Is there enough nutrition to produce reagent? - if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) - continue; - - _hunger.ModifyHunger(uid, -penis.HungerUsage, hunger); - } - - if (!_solutionContainer.ResolveSolution(uid, penis.SolutionName, ref penis.Solution)) - continue; - - _solutionContainer.TryAddReagent(penis.Solution.Value, penis.ReagentId, penis.QuantityPerUpdate, out _); - } - } - - private void AttemptCum(Entity penis, EntityUid userUid, EntityUid containerUid) - { - if (!Resolve(penis, ref penis.Comp)) - return; - - var doargs = new DoAfterArgs(EntityManager, userUid, 5, new CummingDoAfterEvent(), penis, penis, used: containerUid) - { - BreakOnUserMove = true, - BreakOnDamage = true, - BreakOnTargetMove = true, - MovementThreshold = 1.0f, - }; - - _doAfterSystem.TryStartDoAfter(doargs); - } - - private void OnDoAfter(Entity entity, ref CummingDoAfterEvent args) - { - if (args.Cancelled || args.Handled || args.Args.Used == null) - return; - - if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) - return; - - if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) - return; - - args.Handled = true; - var quantity = solution.Volume; - if (quantity == 0) - { - _popupSystem.PopupEntity(Loc.GetString("cum-verb-dry"), entity.Owner, args.Args.User); - return; - } - - if (quantity > targetSolution.AvailableVolume) - quantity = targetSolution.AvailableVolume; - - var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); - _solutionContainer.TryAddSolution(targetSoln.Value, split); - - _popupSystem.PopupEntity(Loc.GetString("cum-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, - args.Args.User, PopupType.Medium); - } - - //Based on BloodstreamSystem.cs - private void OnComponentInit(Entity entity, ref ComponentInit args) - { - var cumSolution = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); - - cumSolution.MaxVolume = entity.Comp.CumMaxVolume; - - // Fill penis solution with CUM - cumSolution.AddReagent(entity.Comp.ReagentId, entity.Comp.CumMaxVolume - cumSolution.Volume); - } - - //Based on UdderSystem.cs - /*TO-DO/Design choices: - * Check for suit (-loincloth) to prevent action? - * Be able to cum on the ground w/o a container. - * -Add a check for a container in the active hand? Yes = fill container. No = Splooge on the ground. - * Better text for actions. (Says you/your instead of the person.) - */ - public void AddCumVerb(Entity entity, ref GetVerbsEvent args) - { - if (args.Using == null || - !args.CanInteract || - !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you cum on the ground? - return; - - var cumContainer = entity.Comp.Solution; - var uid = entity.Owner; - var user = args.User; - var used = args.Using.Value; - - AlternativeVerb verb = new() - { - Act = () => - { - AttemptCum(uid, user, used); - }, - Text = Loc.GetString("cum-verb-get-text"), - Priority = 2 - }; - args.Verbs.Add(verb); - } -} diff --git a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs new file mode 100644 index 00000000000..70060dbeca6 --- /dev/null +++ b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs @@ -0,0 +1,326 @@ +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Popups; +using Content.Shared.Chemistry.Components; +using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; +using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Content.Shared.FloofStation.Traits.Events; +using Robust.Shared.Timing; +using JetBrains.Annotations; + +namespace Content.Server.FloofStation.Traits; + +[UsedImplicitly] +public sealed class LewdTraitSystem : EntitySystem +{ + [Dependency] private readonly HungerSystem _hunger = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + //Initializers + SubscribeLocalEvent(OnComponentInitCum); + SubscribeLocalEvent(OnComponentInitMilk); + //SubscribeLocalEvent(OnComponentInitSquirt); //Unused-Trait is WIP + + //Verbs + SubscribeLocalEvent>(AddCumVerb); + SubscribeLocalEvent>(AddMilkVerb); + //SubscribeLocalEvent>(AddSquirtVerb); //Unused-Trait is WIP + + //Events + SubscribeLocalEvent(OnDoAfterCum); + SubscribeLocalEvent(OnDoAfterMilk); + //SubscribeLocalEvent(OnDoAfterSquirt); //Unused-Trait is WIP + } + + #region event handling + private void OnComponentInitCum(Entity entity, ref ComponentStartup args) + { + var solutionCum = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + solutionCum.MaxVolume = entity.Comp.MaxVolume; + + solutionCum.AddReagent(entity.Comp.ReagentId, entity.Comp.MaxVolume - solutionCum.Volume); + } + + private void OnComponentInitMilk(Entity entity, ref ComponentStartup args) + { + var solutionMilk = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + solutionMilk.MaxVolume = entity.Comp.MaxVolume; + + solutionMilk.AddReagent(entity.Comp.ReagentId, entity.Comp.MaxVolume - solutionMilk.Volume); + } + + //private void OnComponentInitSquirt(Entity entity, ref ComponentStartup args) //Unused-Trait is WIP + //{ + // var solutionSquirt = _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + // solutionSquirt.MaxVolume = entity.Comp.MaxVolume; + + // solutionSquirt.AddReagent(entity.Comp.ReagentId, entity.Comp.MaxVolume - solutionSquirt.Volume); + //} + + public void AddCumVerb(Entity entity, ref GetVerbsEvent args) + { + if (args.Using == null || + !args.CanInteract || + !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. + return; + + _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + + var user = args.User; + var used = args.Using.Value; + + InnateVerb verbCum = new() + { + Act = () => AttemptCum(entity, user, used), + Text = Loc.GetString($"cum-verb-get-text"), + Priority = 1 + }; + args.Verbs.Add(verbCum); + } + + public void AddMilkVerb(Entity entity, ref GetVerbsEvent args) + { + if (args.Using == null || + !args.CanInteract || + !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. + return; + + _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + + var user = args.User; + var used = args.Using.Value; + + InnateVerb verbMilk = new() + { + Act = () => AttemptMilk(entity, user, used), + Text = Loc.GetString($"milk-verb-get-text"), + Priority = 1 + }; + args.Verbs.Add(verbMilk); + } + + //public void AddSquirtVerb(Entity entity, ref GetVerbsEvent args) //Unused-Trait is WIP + //{ + // if (args.Using == null || + // !args.CanInteract || + // !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. + // return; + + // _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionName); + + // var user = args.User; + // var used = args.Using.Value; + + // InnateVerb verbSquirt = new() + // { + // Act = () => AttemptSquirt(entity, user, used), + // Text = Loc.GetString($"squirt-verb-get-text"), + // Priority = 1 + // }; + // args.Verbs.Add(verbSquirt); + //} + + private void OnDoAfterCum(Entity entity, ref CummingDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Used == null) + return; + + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) + return; + + if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) + return; + + args.Handled = true; + var quantity = solution.Volume; + if (quantity == 0) + { + _popupSystem.PopupEntity(Loc.GetString("cum-verb-dry"), entity.Owner, args.Args.User); + return; + } + + if (quantity > targetSolution.AvailableVolume) + quantity = targetSolution.AvailableVolume; + + var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); + _solutionContainer.TryAddSolution(targetSoln.Value, split); + _popupSystem.PopupEntity(Loc.GetString("cum-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, args.Args.User, PopupType.Medium); + } + + private void OnDoAfterMilk(Entity entity, ref MilkingDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Args.Used == null) + return; + + if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) + return; + + if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) + return; + + args.Handled = true; + var quantity = solution.Volume; + if (quantity == 0) + { + _popupSystem.PopupEntity(Loc.GetString("milk-verb-dry"), entity.Owner, args.Args.User); + return; + } + + if (quantity > targetSolution.AvailableVolume) + quantity = targetSolution.AvailableVolume; + + var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); + _solutionContainer.TryAddSolution(targetSoln.Value, split); + _popupSystem.PopupEntity(Loc.GetString("milk-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, args.Args.User, PopupType.Medium); + } + + //private void OnDoAfterSquirt(Entity entity, ref SquirtingDoAfterEvent args) //Unused-Trait is WIP + //{ + // if (args.Cancelled || args.Handled || args.Args.Used == null) + // return; + + // if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) + // return; + + // if (!_solutionContainer.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution)) + // return; + + // args.Handled = true; + // var quantity = solution.Volume; + // if (quantity == 0) + // { + // _popupSystem.PopupEntity(Loc.GetString("squirt-verb-dry"), entity.Owner, args.Args.User); + // return; + // } + + // if (quantity > targetSolution.AvailableVolume) + // quantity = targetSolution.AvailableVolume; + + // var split = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, quantity); + // _solutionContainer.TryAddSolution(targetSoln.Value, split); + // _popupSystem.PopupEntity(Loc.GetString("squirt-verb-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner, args.Args.User, PopupType.Medium); + //} + #endregion + + #region utilities + private void AttemptCum(Entity lewd, EntityUid userUid, EntityUid containerUid) + { + if (!HasComp(userUid)) + return; + + var doargs = new DoAfterArgs(EntityManager, userUid, 5, new CummingDoAfterEvent(), lewd, lewd, used: containerUid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }; + + _doAfterSystem.TryStartDoAfter(doargs); + } + + private void AttemptMilk(Entity lewd, EntityUid userUid, EntityUid containerUid) + { + if (!HasComp(userUid)) + return; + + var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), lewd, lewd, used: containerUid) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f, + }; + + _doAfterSystem.TryStartDoAfter(doargs); + } + + //private void AttemptSquirt(Entity lewd, EntityUid userUid, EntityUid containerUid) //Unused-Trait is WIP + //{ + // if (!HasComp(userUid)) + // return; + + // var doargs = new DoAfterArgs(EntityManager, userUid, 5, new SquirtingDoAfterEvent(), lewd, lewd, used: containerUid) + // { + // BreakOnUserMove = true, + // BreakOnDamage = true, + // BreakOnTargetMove = true, + // MovementThreshold = 1.0f, + // }; + + // _doAfterSystem.TryStartDoAfter(doargs); + //} + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var now = _timing.CurTime; + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var containerCum, out var containerMilk, out var containerSquirt)) + { + if (_mobState.IsDead(uid)) + continue; + + if (!(now < containerCum.NextGrowth)) + { + containerCum.NextGrowth = now + containerCum.GrowthDelay; + + // Actually there is food digestion so no problem with instant reagent generation "OnFeed" + if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + { + // Is there enough nutrition to produce reagent? + if (!(_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)) + _hunger.ModifyHunger(uid, -containerCum.HungerUsage, hunger); + } + + if (_solutionContainer.ResolveSolution(uid, containerCum.SolutionName, ref containerCum.Solution)) + _solutionContainer.TryAddReagent(containerCum.Solution.Value, containerCum.ReagentId, containerCum.QuantityPerUpdate, out _); + } + + if (!(now < containerMilk.NextGrowth)) + { + containerMilk.NextGrowth = now + containerMilk.GrowthDelay; + + if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + { + if (!(_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)) + _hunger.ModifyHunger(uid, -containerMilk.HungerUsage, hunger); + } + + if (_solutionContainer.ResolveSolution(uid, containerMilk.SolutionName, ref containerMilk.Solution)) + _solutionContainer.TryAddReagent(containerMilk.Solution.Value, containerMilk.ReagentId, containerMilk.QuantityPerUpdate, out _); + } + + //if (!(now < containerSquirt.NextGrowth)) //Unused-Trait is WIP + //{ + // containerSquirt.NextGrowth = now + containerSquirt.GrowthDelay; + + // + // if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + // { + // + // if (!(_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)) + // _hunger.ModifyHunger(uid, -containerSquirt.HungerUsage, hunger); + // } + + // if (_solutionContainer.ResolveSolution(uid, containerSquirt.SolutionName, ref containerSquirt.Solution)) + // _solutionContainer.TryAddReagent(containerSquirt.Solution.Value, containerSquirt.ReagentId, containerSquirt.QuantityPerUpdate, out _); + //} + } + } + #endregion +} diff --git a/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs b/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs index 171a904eff3..418afed2185 100644 --- a/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs +++ b/Content.Shared/Floofstation/Traits/Events/CummingDoAfterEvent.cs @@ -7,3 +7,4 @@ namespace Content.Shared.FloofStation.Traits.Events; public sealed partial class CummingDoAfterEvent : SimpleDoAfterEvent { } + diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 6b5c5d4422a..c8c6f464680 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -73,11 +73,11 @@ trait-description-Foreigner = For one reason or another you do not speak this station's primary language. Instead, you have a translator issued to you that only you can use. -trait-name-HasPenis = Cock -trait-description-HasPenis = You have a schlong between your legs. +trait-name-CumProducer = Cock +trait-description-CumProducer = You have a schlong between your legs. -trait-name-HasBoobs = Boobs -trait-description-HasBoobs = You have a pair of large mammaries. +trait-name-MilkProducer = Boobs +trait-description-MilkProducer = You have a pair of large mammaries. -trait-name-HasVagina = Pussy -trait-description-HasVagina = You have a slit between your legs. +trait-name-SquirtProducer = Pussy +trait-description-SquirtProducer = You have a slit between your legs. diff --git a/Resources/Prototypes/FloofStation/Traits/lewd.yml b/Resources/Prototypes/FloofStation/Traits/lewd.yml index 08bfef7cad6..d3cd1304479 100644 --- a/Resources/Prototypes/FloofStation/Traits/lewd.yml +++ b/Resources/Prototypes/FloofStation/Traits/lewd.yml @@ -1,5 +1,5 @@ - type: trait - id: HasPenis + id: CumProducer category: Physical requirements: - !type:CharacterJobRequirement @@ -8,7 +8,8 @@ - Borg - MedicalBorg components: - - type: HasPenis + - type: CumProducer + solutionname: "penis" - type: SolutionContainerManager solutions: penis: @@ -18,7 +19,7 @@ Quantity: 30 - type: trait - id: HasBoobs + id: MilkProducer category: Physical requirements: - !type:CharacterJobRequirement @@ -27,7 +28,8 @@ - Borg - MedicalBorg components: - - type: HasBoobs + - type: MilkProducer + solutionname: "breasts" - type: SolutionContainerManager solutions: breasts: @@ -36,8 +38,9 @@ - ReagentId: Milk Quantity: 30 +# WIP - Needs a Reagent # - type: trait -# id: HasVagina +# id: SquirtProducer # category: Physical # requirements: # - !type:CharacterJobRequirement @@ -46,4 +49,12 @@ # - Borg # - MedicalBorg # components: -# - type: HasVagina +# - type: SquirtProducer +# solutionname: "vagina" +# - type: SolutionContainerManager +# solutions: +# vagina: +# maxVol: 250 +# reagents: +# - ReagentId: Water +# Quantity: 30 From 4b9f164cf4738581eb9d4c68b5e86af9be62e84d Mon Sep 17 00:00:00 2001 From: Pierson Arnold Date: Mon, 5 Aug 2024 14:39:50 -0500 Subject: [PATCH 3/5] Fixing lewdtraitsystem --- Content.Server/FloofStation/Traits/LewdTraitSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs index 70060dbeca6..063688a0fca 100644 --- a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs +++ b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs @@ -268,9 +268,9 @@ public override void Update(float frameTime) base.Update(frameTime); var now = _timing.CurTime; - var query = AllEntityQuery(); + var query = AllEntityQuery(); //SquirtProducerComponent -unused - while (query.MoveNext(out var uid, out var containerCum, out var containerMilk, out var containerSquirt)) + while (query.MoveNext(out var uid, out var containerCum, out var containerMilk)) // out var containerSquirt -unused { if (_mobState.IsDead(uid)) continue; From ab527b0212c239899e3b2cb3710ff5d7e509c01a Mon Sep 17 00:00:00 2001 From: Memeji Dankiri <39416235+Memeji@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:56:29 -0500 Subject: [PATCH 4/5] Update Content.Server/FloofStation/Traits/LewdTraitSystem.cs Co-authored-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> --- Content.Server/FloofStation/Traits/LewdTraitSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs index 063688a0fca..61ed4f9fa0a 100644 --- a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs +++ b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs @@ -73,6 +73,7 @@ public void AddCumVerb(Entity entity, ref GetVerbsEvent(args.Using.Value)) //see if removing this part lets you milk on the ground. return; From a9733ef4fb6ac332cc03d3c66fd61b81b3adabbc Mon Sep 17 00:00:00 2001 From: Memeji Dankiri <39416235+Memeji@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:56:35 -0500 Subject: [PATCH 5/5] Update Content.Server/FloofStation/Traits/LewdTraitSystem.cs Co-authored-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> --- Content.Server/FloofStation/Traits/LewdTraitSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs index 61ed4f9fa0a..1429488a47a 100644 --- a/Content.Server/FloofStation/Traits/LewdTraitSystem.cs +++ b/Content.Server/FloofStation/Traits/LewdTraitSystem.cs @@ -95,6 +95,7 @@ public void AddMilkVerb(Entity entity, ref GetVerbsEvent< { if (args.Using == null || !args.CanInteract || + args.User != args.Target || !EntityManager.HasComponent(args.Using.Value)) //see if removing this part lets you milk on the ground. return;