diff --git a/Content.Server/Lube/LubeSystem.cs b/Content.Server/Lube/LubeSystem.cs new file mode 100644 index 0000000000..972da9b908 --- /dev/null +++ b/Content.Server/Lube/LubeSystem.cs @@ -0,0 +1,72 @@ +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Nutrition.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Item; +using Content.Shared.Lube; +using Content.Shared.Popups; +using Robust.Shared.Random; + +namespace Content.Server.Lube; + +public sealed class LubeSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteract); + } + + // When glue bottle is used on item it will apply the glued and unremoveable components. + private void OnInteract(EntityUid uid, LubeComponent component, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (!args.CanReach || args.Target is not { Valid: true } target) + return; + + if (TryComp(uid, out var drink) && !drink.Opened) + { + return; + } + + if (TryLube(uid, component, target)) + { + args.Handled = true; + _audio.PlayPvs(component.Squeeze, uid); + _popup.PopupEntity(Loc.GetString("lube-success", ("target", Identity.Entity(target, EntityManager))), args.User, args.User, PopupType.Medium); + } + else + { + _popup.PopupEntity(Loc.GetString("lube-failure", ("target", Identity.Entity(target, EntityManager))), args.User, args.User, PopupType.Medium); + } + } + + private bool TryLube(EntityUid uid, LubeComponent component, EntityUid target) + { + if (HasComp(target) || !HasComp(target)) + { + return false; + } + + if (HasComp(target) && _solutionContainer.TryGetSolution(uid, component.Solution, out var solution)) + { + var quantity = solution.RemoveReagent(component.Reagent, component.Consumption); + if (quantity > 0) + { + var lubed = EnsureComp(target); + lubed.SlipsLeft = _random.Next(component.MinSlips * quantity.Int(), component.MaxSlips * quantity.Int()); + lubed.SlipStrength = component.SlipStrength; + return true; + } + } + return false; + } +} diff --git a/Content.Server/Lube/LubedSystem.cs b/Content.Server/Lube/LubedSystem.cs new file mode 100644 index 0000000000..d81005e631 --- /dev/null +++ b/Content.Server/Lube/LubedSystem.cs @@ -0,0 +1,49 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Lube; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Robust.Shared.Containers; +using Robust.Shared.Random; + +namespace Content.Server.Lube; + +public sealed class LubedSystem : EntitySystem +{ + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnHandPickUp); + } + + private void OnInit(EntityUid uid, LubedComponent component, ComponentInit args) + { + var meta = MetaData(uid); + var name = meta.EntityName; + component.BeforeLubedEntityName = meta.EntityName; + _metaData.SetEntityName(uid, Loc.GetString("lubed-name-prefix", ("target", name))); + } + + private void OnHandPickUp(EntityUid uid, LubedComponent component, ContainerGettingInsertedAttemptEvent args) + { + if (component.SlipsLeft <= 0) + { + RemComp(uid); + _metaData.SetEntityName(uid, component.BeforeLubedEntityName); + return; + } + component.SlipsLeft--; + args.Cancel(); + var user = args.Container.Owner; + _transform.SetCoordinates(uid, Transform(user).Coordinates); + _throwing.TryThrow(uid, _random.NextVector2(), strength: component.SlipStrength); + _popup.PopupEntity(Loc.GetString("lube-slip", ("target", Identity.Entity(uid, EntityManager))), user, user, PopupType.MediumCaution); + } +} diff --git a/Content.Shared/Lube/LubeComponent.cs b/Content.Shared/Lube/LubeComponent.cs new file mode 100644 index 0000000000..d945f14265 --- /dev/null +++ b/Content.Shared/Lube/LubeComponent.cs @@ -0,0 +1,47 @@ +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Lube; + +[RegisterComponent, NetworkedComponent] +public sealed class LubeComponent : Component +{ + [DataField("squeeze")] + public SoundSpecifier Squeeze = new SoundPathSpecifier("/Audio/Items/squeezebottle.ogg"); + + /// + /// Solution on the entity that contains the glue. + /// + [DataField("solution")] + public string Solution = "drink"; + + /// + /// Reagent that will be used as glue. + /// + [DataField("reagent", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Reagent = "SpaceLube"; + + /// + /// Reagent consumption per use. + /// + [DataField("consumption"), ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 Consumption = FixedPoint2.New(3); + + /// + /// Min slips per unit + /// + [DataField("minSlips"), ViewVariables(VVAccess.ReadWrite)] + public int MinSlips = 1; + + /// + /// Max slips per unit + /// + [DataField("maxSlips"), ViewVariables(VVAccess.ReadWrite)] + public int MaxSlips = 6; + + [DataField("slipStrength"), ViewVariables(VVAccess.ReadWrite)] + public int SlipStrength = 10; +} diff --git a/Content.Shared/Lube/LubedComponent.cs b/Content.Shared/Lube/LubedComponent.cs new file mode 100644 index 0000000000..1fd3322dc8 --- /dev/null +++ b/Content.Shared/Lube/LubedComponent.cs @@ -0,0 +1,17 @@ +namespace Content.Shared.Lube; + +[RegisterComponent] +public sealed class LubedComponent : Component +{ + /// + /// Reverts name to before prefix event (essentially removes prefix). + /// + [DataField("beforeLubedEntityName")] + public string BeforeLubedEntityName = string.Empty; + + [DataField("slipsLeft"), ViewVariables(VVAccess.ReadWrite)] + public int SlipsLeft; + + [DataField("slipStrength"), ViewVariables(VVAccess.ReadWrite)] + public int SlipStrength; +} diff --git a/Resources/Locale/en-US/lube/lube.ftl b/Resources/Locale/en-US/lube/lube.ftl new file mode 100644 index 0000000000..57af8f774a --- /dev/null +++ b/Resources/Locale/en-US/lube/lube.ftl @@ -0,0 +1,4 @@ +lube-success = {THE($target)} has been covered in lube! +lubed-name-prefix = Lubed {$target} +lube-failure = Can't cover {THE($target)} in lube! +lube-slip = {THE($target)} slips out of your hands! diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index e02d7c3076..6396a52834 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -129,6 +129,8 @@ prob: 0.20 - id: DrinkSpaceGlue prob: 0.20 + - id: DrinkSpaceLube + prob: 0.20 - type: entity id: ClosetWallMaintenanceFilledRandom diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml index 398c7e4351..cfdc5a6b54 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml @@ -29,3 +29,36 @@ - type: SolutionContainerVisuals maxFillLevels: 6 fillBaseName: fill + +- type: entity + parent: DrinkBase + id: DrinkSpaceLube + name: space lube tube + description: High performance lubricant intended for maintenance of extremely complex mechanical equipment. + components: + - type: Drink + isOpen: false + openSounds: + collection: packetOpenSounds + - type: Sprite + sprite: Objects/Consumable/Drinks/lube-tube.rsi + layers: + - state: icon + map: [ "enum.SolutionContainerLayers.Base" ] + - state: fill1 + map: [ "enum.SolutionContainerLayers.Fill" ] + visible: false + - state: icon-front + map: [ "enum.SolutionContainerLayers.Overlay" ] + - type: Appearance + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: SpaceLube + Quantity: 30 + - type: SolutionContainerVisuals + maxFillLevels: 6 + fillBaseName: fill + - type: Lube diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill1.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill1.png new file mode 100644 index 0000000000..d96e474781 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill1.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill2.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill2.png new file mode 100644 index 0000000000..0134a590cc Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill2.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill3.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill3.png new file mode 100644 index 0000000000..4731349b16 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill3.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill4.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill4.png new file mode 100644 index 0000000000..0acebd225e Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill4.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill5.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill5.png new file mode 100644 index 0000000000..9e5b326d35 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill5.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill6.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill6.png new file mode 100644 index 0000000000..1bd4eac95c Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/fill6.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon-front.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon-front.png new file mode 100644 index 0000000000..bd5ca47233 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon-front.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon.png b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon.png new file mode 100644 index 0000000000..2dd92fe1f0 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/meta.json new file mode 100644 index 0000000000..734e9e1e64 --- /dev/null +++ b/Resources/Textures/Objects/Consumable/Drinks/lube-tube.rsi/meta.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-NC-SA-4.0", + "copyright": "Created by discord: brainfood#7460 / github: brainfood1183.", + "states": [ + { + "name": "icon" + }, + { + "name": "icon-front" + }, + { + "name": "fill1" + }, + { + "name": "fill2" + }, + { + "name": "fill3" + }, + { + "name": "fill4" + }, + { + "name": "fill5" + }, + { + "name": "fill6" + } + ] +}