-
Notifications
You must be signed in to change notification settings - Fork 86
Milking Machine! #408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Compilatron144
wants to merge
4
commits into
Floof-Station:master
Choose a base branch
from
Compilatron144:MilkingMachine
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Milking Machine! #408
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
Content.Shared/_Euphoria/Lewd/Milker/Components/MilkerComponent.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using Content.Shared.FixedPoint; | ||
| using Content.Shared.Whitelist; | ||
| using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; | ||
| using Robust.Shared.GameStates; | ||
| using Robust.Shared.Utility; | ||
| using System.Numerics; | ||
|
|
||
| namespace Content.Shared._Floof.Lewd.Milker; | ||
|
|
||
| [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] | ||
| public sealed partial class MilkerComponent : Component | ||
| { | ||
| [DataField, AutoNetworkedField] | ||
| public EntityUid? MilkedEntity; // Entity being milked | ||
| [DataField] | ||
| public EntityWhitelist MilkedEntityWhitelist; // Whitelist for valid milkable entities | ||
| [DataField] | ||
| public TimeSpan AttachDelay = TimeSpan.FromSeconds(5); // Delay to attach an entity | ||
| [DataField] | ||
| public string? MilkedSolution; // Solution of entity being milked | ||
| [DataField] | ||
| public string[] MilkedSolutionWhitelist = ["breasts", "udder"]; | ||
| [DataField] | ||
| public FixedPoint2 MilkedAmount = 10; // Amount to be milked | ||
| [DataField] | ||
| public string PopupDataset; | ||
| [DataField] | ||
| public double PopupChance = 0.25; | ||
| [DataField] | ||
| public TimeSpan UpdateDelay = TimeSpan.FromSeconds(5); | ||
| [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] | ||
| public TimeSpan NextUpdate = TimeSpan.FromSeconds(0); | ||
| [DataField] | ||
| public string Solution = "tank"; // Solution for fluids to be transfered too | ||
| [DataField] | ||
| public SpriteSpecifier TubeSprite = default!; // Sprite for the connection tube | ||
| [DataField] | ||
| public double TubeLength = 5; // Maximum length of the tube | ||
| [DataField] | ||
| public Vector2 TubeOffsetSource, TubeOffsetTarget = Vector2.Zero; | ||
| public const string VisualsContainerName = "tube-visuals"; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| using Content.Shared.DoAfter; | ||
| using Robust.Shared.Serialization; | ||
|
|
||
| namespace Content.Shared._Floof.Lewd.Milker; | ||
|
|
||
| [Serializable, NetSerializable] | ||
| public sealed partial class MilkerDoAfterEvent : SimpleDoAfterEvent | ||
| { | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| using Content.Shared.DragDrop; | ||
| using Content.Shared.Whitelist; | ||
| using Content.Shared.Chemistry.EntitySystems; | ||
| using Content.Shared.ActionBlocker; | ||
| using Content.Shared.Interaction; | ||
| using Content.Shared.DoAfter; | ||
| using Content.Shared.Chemistry.Components; | ||
| using Content.Shared.IdentityManagement; | ||
| using Content.Shared.Popups; | ||
| using Content.Shared.Dataset; | ||
| using Content.Shared.Destructible; | ||
| using Robust.Shared.Timing; | ||
| using System.Numerics; | ||
| using Content.Shared.Power.EntitySystems; | ||
| using Robust.Shared.Prototypes; | ||
| using Robust.Shared.Random; | ||
| using Content.Shared.Audio; | ||
| using Content.Shared.Random.Helpers; | ||
| using Robust.Shared.Containers; | ||
| using Content.Shared._Floof.Leash.Components; | ||
| using Robust.Shared.Network; | ||
| using System.Linq; | ||
|
|
||
| namespace Content.Shared._Floof.Lewd.Milker; | ||
|
|
||
| [Serializable] | ||
| public sealed class MilkerSystem : EntitySystem | ||
| { | ||
| [Dependency] private readonly IGameTiming _timing = default!; | ||
| [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; | ||
| [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; | ||
| [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; | ||
| [Dependency] private readonly SharedInteractionSystem _interaction = default!; | ||
| [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; | ||
| [Dependency] private readonly SharedPopupSystem _popupSystem = default!; | ||
| [Dependency] private readonly SharedTransformSystem _xform = default!; | ||
| [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; | ||
| [Dependency] private readonly IPrototypeManager _prototypeManager = default!; | ||
| [Dependency] private readonly IRobustRandom _random = default!; | ||
| [Dependency] private readonly SharedAmbientSoundSystem _ambient = default!; | ||
| [Dependency] private readonly SharedContainerSystem _container = default!; | ||
| [Dependency] private readonly INetManager _net = default!; | ||
| public override void Initialize() | ||
| { | ||
| base.Initialize(); | ||
| SubscribeLocalEvent<MilkerComponent, ComponentStartup>(OnComponentInit); | ||
| SubscribeLocalEvent<MilkerComponent, CanDropTargetEvent>(OnCanDrop); | ||
| SubscribeLocalEvent<MilkerComponent, DragDropTargetEvent>(OnDragDropped); | ||
| SubscribeLocalEvent<MilkerComponent, InteractHandEvent>(OnInteractHand); | ||
| SubscribeLocalEvent<MilkerComponent, MilkerDoAfterEvent>(FinishAttaching); | ||
| SubscribeLocalEvent<MilkerComponent, DestructionEventArgs>(OnDestroyed); | ||
| } | ||
|
|
||
| private void OnComponentInit(Entity<MilkerComponent> entity, ref ComponentStartup args) | ||
| { | ||
| base.Initialize(); | ||
| } | ||
|
|
||
| private void OnDestroyed(Entity<MilkerComponent> entity, ref DestructionEventArgs args) | ||
| { | ||
| Detach(entity); | ||
| } | ||
|
|
||
| private void OnCanDrop(Entity<MilkerComponent> entity, ref CanDropTargetEvent args) | ||
| { | ||
| args.Handled = true; | ||
| args.CanDrop |= CheckInteraction(entity, args.Dragged, args.User); | ||
| } | ||
|
|
||
| private void OnDragDropped(Entity<MilkerComponent> entity, ref DragDropTargetEvent args) | ||
| { | ||
| args.Handled = true; | ||
| TryAttaching(entity, args.Dragged, args.User); | ||
|
|
||
| } | ||
|
|
||
| private void OnInteractHand(Entity<MilkerComponent> entity, ref InteractHandEvent args) | ||
| { | ||
| TryAttaching(entity, args.User, args.User); | ||
| } | ||
|
|
||
| public void TryAttaching(Entity<MilkerComponent> entity, EntityUid target, EntityUid user) | ||
| { | ||
| if (!CheckInteraction(entity, target, user)) | ||
| return; | ||
|
|
||
| if (entity.Comp.MilkedEntity != null) | ||
| { | ||
| Detach(entity); | ||
| } | ||
| else if (CheckMilkable(entity, target)) | ||
| { | ||
| var doAfterArgs = new DoAfterArgs(EntityManager, user, entity.Comp.AttachDelay, new MilkerDoAfterEvent(), entity, target) | ||
| { | ||
| BreakOnMove = true, | ||
| BreakOnDamage = true, | ||
| AttemptFrequency = AttemptFrequency.EveryTick | ||
| }; | ||
| _popupSystem.PopupPredicted(Loc.GetString("milker-attach-start-popup", | ||
| ("user", Identity.Entity(user, EntityManager)), | ||
| ("target", Identity.Entity(target, EntityManager)), | ||
| ("entity", Identity.Entity(entity, EntityManager)) | ||
| ), entity.Owner, user); | ||
| _doAfter.TryStartDoAfter(doAfterArgs); | ||
| } | ||
| else | ||
| { | ||
| // Only shown to the person who attempted to attach them | ||
| _popupSystem.PopupClient(Loc.GetString("milker-attach-fail-popup", | ||
| ("target", Identity.Entity(target, EntityManager)) | ||
| ), entity.Owner, user); | ||
| } | ||
| } | ||
|
|
||
| void FinishAttaching(Entity<MilkerComponent> entity, ref MilkerDoAfterEvent args) | ||
| { | ||
| if (!args.Cancelled && args.Target != null) | ||
| { | ||
| Attach(entity, (EntityUid)args.Target); | ||
| } | ||
| } | ||
|
|
||
| public void Attach(Entity<MilkerComponent> entity, EntityUid target) | ||
| { | ||
| _popupSystem.PopupPredicted(Loc.GetString("milker-attach-finish-popup", | ||
| ("target", Identity.Entity(target, EntityManager)), | ||
| ("entity", Identity.Entity(entity, EntityManager)) | ||
| ), entity.Owner, target); | ||
| _ambient.SetAmbience(entity, true); | ||
| entity.Comp.MilkedSolution = _solution.EnumerateSolutions(target).First((solution) => entity.Comp.MilkedSolutionWhitelist.Contains(solution.Name)).Name; | ||
| entity.Comp.MilkedEntity = target; | ||
|
|
||
| if (entity.Comp.TubeSprite is { } sprite) | ||
| { | ||
| _container.EnsureContainer<ContainerSlot>(entity, MilkerComponent.VisualsContainerName); | ||
| if (EntityManager.TrySpawnInContainer(null, entity, MilkerComponent.VisualsContainerName, out var visualEntity)) | ||
| { | ||
| var visualComp = EnsureComp<LeashedVisualsComponent>(visualEntity.Value); | ||
| visualComp.Sprite = sprite; | ||
| visualComp.Source = entity; | ||
| visualComp.Target = target; | ||
| } | ||
| } | ||
| Dirty(entity); | ||
| } | ||
|
|
||
| public void Detach(Entity<MilkerComponent> entity) | ||
| { | ||
| if (entity.Comp.MilkedEntity != null) | ||
| { | ||
| _popupSystem.PopupPredicted(Loc.GetString("milker-detach-popup", | ||
| ("target", Identity.Entity((EntityUid)entity.Comp.MilkedEntity, EntityManager)), | ||
| ("entity", Identity.Entity(entity, EntityManager)) | ||
| ), entity.Owner, entity.Comp.MilkedEntity); | ||
| _ambient.SetAmbience(entity, false); | ||
| entity.Comp.MilkedSolution = null; | ||
| entity.Comp.MilkedEntity = null; | ||
| if (_container.TryGetContainer(entity, MilkerComponent.VisualsContainerName, out var visualsContainer)) | ||
| _container.CleanContainer(visualsContainer); | ||
| Dirty(entity); | ||
| } | ||
| } | ||
|
|
||
| public override void Update(float frameTime) | ||
| { | ||
| base.Update(frameTime); | ||
|
|
||
| var query = EntityQueryEnumerator<MilkerComponent>(); | ||
| while (query.MoveNext(out var uid, out var component)) | ||
| { | ||
| // If not attached to anything, skip | ||
| if (component.MilkedEntity == null) | ||
| continue; | ||
|
|
||
| // If the milked entity is too far away, detach them | ||
| if (Vector2.DistanceSquared( | ||
| _xform.GetWorldPosition(uid), | ||
| _xform.GetWorldPosition((EntityUid)component.MilkedEntity)) | ||
| > component.TubeLength * component.TubeLength) | ||
| { | ||
| Detach(new Entity<MilkerComponent>(uid, component)); | ||
| continue; | ||
| } | ||
|
|
||
| // Check if we have power (also should check if the power switch is on) | ||
| if (!_powerReceiver.IsPowered(uid)) | ||
| continue; | ||
|
|
||
| // Check if it's time for our next tick | ||
| if (_timing.CurTime < component.NextUpdate) | ||
| continue; | ||
| component.NextUpdate = _timing.CurTime + component.UpdateDelay; | ||
|
|
||
| // Do the milking | ||
| _solution.TryGetSolution(uid, component.Solution, out Entity<SolutionComponent>? solution); | ||
| _solution.TryGetSolution((EntityUid)component.MilkedEntity, component.MilkedSolution, out Entity<SolutionComponent>? source); | ||
| if (solution == null || source == null) | ||
| continue; | ||
| _solution.TryTransferSolution( | ||
| (Entity<SolutionComponent>)solution, | ||
| ((SolutionComponent)source).Solution, | ||
| component.MilkedAmount); | ||
|
|
||
| // Display some supportive prompts | ||
| _prototypeManager.TryIndex<LocalizedDatasetPrototype>(component.PopupDataset, out LocalizedDatasetPrototype? dataset); | ||
| if (dataset != null && _random.NextFloat() < component.PopupChance) | ||
| _popupSystem.PopupClient(Loc.GetString(_random.Pick(dataset.Values)), (EntityUid)component.MilkedEntity, (EntityUid)component.MilkedEntity, PopupType.Medium); | ||
| } | ||
| } | ||
|
|
||
| // Checks if the entity is whitelist valid - can we milk this twink? | ||
| public bool CheckMilkable(Entity<MilkerComponent> milker, EntityUid target) | ||
| { | ||
| if (!_whitelist.IsWhitelistPass(milker.Comp.MilkedEntityWhitelist, target)) | ||
| return false; | ||
| if (!_solution.EnumerateSolutions(target).Any((solution) => milker.Comp.MilkedSolutionWhitelist.Contains(solution.Name))) | ||
| return false; | ||
| return true; | ||
| } | ||
|
|
||
| // Checks if the entity can actually be reached by the milker and milkee | ||
| public bool CheckInteraction(Entity<MilkerComponent> milker, EntityUid target, EntityUid user) | ||
| { | ||
| if (!_actionBlocker.CanComplexInteract(user)) | ||
| return false; | ||
|
|
||
| if (!_interaction.InRangeUnobstructed(user, target)) | ||
| return false; | ||
| return true; | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| milker-attach-start-popup = {THE($user)} is attaching {THE($target)} to {THE($entity)} | ||
| milker-attach-fail-popup = {THE($target)} can't be milked! | ||
| milker-attach-finish-popup = {THE($target)} is attached to {THE($entity)} | ||
|
|
||
| milker-detach-popup = {THE($target)} is detached from {THE($entity)} | ||
|
|
||
| milker-popup-1 = You feel the milker tug you | ||
| milker-popup-2 = You feel the pressure on your chest slowly relieved | ||
| milker-popup-3 = Moo | ||
| milker-popup-4 = Moo! | ||
| milker-popup-5 = Moo... | ||
| milker-popup-6 = Moo? | ||
| milker-popup-7 = The milker tugs your nipples | ||
| milker-popup-8 = The cups suckle your breasts | ||
| milker-popup-9 = You feel like a cow. Moo. | ||
| milker-popup-10 = You have the urge to moo | ||
| milker-popup-11 = The pressure in your breasts quickly turns to pleasure | ||
| milker-popup-12 = Your nipples are tingling | ||
| milker-popup-13 = Pump. Pump. Pump. | ||
| milker-popup-14 = You feel the suction in time with your heartbeat | ||
| milker-popup-15 = Your nipples feel numb | ||
| milker-popup-16 = Warmth radiates through your chest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
Resources/Prototypes/_Euphoria/Entities/Objects/Devices/Circuitboards/Machine/lewd.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| - type: entity | ||
| id: MilkingMachineCircuitboard | ||
| parent: BaseMachineCircuitboard | ||
| name: milking machine board | ||
| description: A machine printed circuit board for a milking machine. | ||
| components: | ||
| - type: Sprite | ||
| state: engineering | ||
| - type: MachineBoard | ||
| prototype: MilkingMachine | ||
| stackRequirements: | ||
| Plastic: 4 | ||
| Glass: 4 | ||
| componentRequirements: | ||
| GasVolumePump: | ||
| amount: 1 | ||
| defaultPrototype: GasVolumePump | ||
| tagRequirements: | ||
| Bucket: | ||
| amount: 2 | ||
| defaultPrototype: Bucket |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also please move this to the beginning of the
whileloop body. The methods you're invoking before doing this check are way too expensive too run 60 times a second (300 times more than necessary) for every milking machine entity