diff --git a/Content.Server/Glue/GlueSystem.cs b/Content.Server/Glue/GlueSystem.cs index fbd2800704..1017c7290a 100644 --- a/Content.Server/Glue/GlueSystem.cs +++ b/Content.Server/Glue/GlueSystem.cs @@ -1,59 +1,106 @@ using Content.Shared.IdentityManagement; using Content.Shared.Popups; using Content.Shared.Item; -using Content.Shared.Interaction.Components; using Content.Shared.Glue; -using Content.Server.Nutrition.EntitySystems; using Content.Shared.Interaction; +using Content.Server.Chemistry.EntitySystems; using Content.Server.Nutrition.Components; +using Content.Shared.Hands; +using Robust.Shared.Timing; +using Content.Shared.Interaction.Components; -namespace Content.Server.Glue +namespace Content.Server.Glue; + +public sealed class GlueSystem : SharedGlueSystem { - public sealed class GlueSystem : EntitySystem + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + + public override void Initialize() { - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly FoodSystem _food = default!; + base.Initialize(); + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnGluedInit); + SubscribeLocalEvent(OnHandPickUp); + } - public override void Initialize() + // When glue bottle is used on item it will apply the glued and unremoveable components. + private void OnInteract(EntityUid uid, GlueComponent 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) { - base.Initialize(); - - SubscribeLocalEvent(OnInteract); + return; } - // When glue bottle is used on item it will apply the glued and unremoveable components. - private void OnInteract(EntityUid uid, GlueComponent component, AfterInteractEvent args) + if (TryGlue(uid, component, target)) { - if (args.Handled) - return; - - if (!args.CanReach || args.Target is not { Valid: true } target) - return; - - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("glue-failure", ("target", Identity.Entity(target, EntityManager))), args.User, - args.User, PopupType.Medium); - return; - } - - if (HasComp(target)) - { - _audio.PlayPvs(component.Squeeze, uid); - _popup.PopupEntity(Loc.GetString("glue-success", ("target", Identity.Entity(target, EntityManager))), args.User, - args.User, PopupType.Medium); - EnsureComp(target); - } - - if (TryComp(uid, out var food)) - { - _food.DeleteAndSpawnTrash(food, uid, args.User); - } - args.Handled = true; + _audio.PlayPvs(component.Squeeze, uid); + _popup.PopupEntity(Loc.GetString("glue-success", ("target", target)), args.User, args.User, PopupType.Medium); + } + else + { + _popup.PopupEntity(Loc.GetString("glue-failure", ("target", target)), args.User, args.User, PopupType.Medium); + } + } + + private bool TryGlue(EntityUid uid, GlueComponent component, EntityUid target) + { + // if item is glued then don't apply glue again so it can be removed for reasonable time + 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.ConsumptionUnit); + if (quantity > 0) + { + EnsureComp(target).Duration = quantity.Double() * component.DurationPerUnit; + return true; + } + } + return false; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var glue, out _)) + { + if (_timing.CurTime < glue.Until) + continue; + + _metaData.SetEntityName(uid, glue.BeforeGluedEntityName); + RemComp(uid); + RemComp(uid); + } + } + + private void OnGluedInit(EntityUid uid, GluedComponent component, ComponentInit args) + { + var meta = MetaData(uid); + var name = meta.EntityName; + component.BeforeGluedEntityName = meta.EntityName; + _metaData.SetEntityName(uid, Loc.GetString("glued-name-prefix", ("target", name))); + } + + private void OnHandPickUp(EntityUid uid, GluedComponent component, GotEquippedHandEvent args) + { + EnsureComp(uid); + component.Until = _timing.CurTime + component.Duration; } } diff --git a/Content.Server/Glue/GluedSystem.cs b/Content.Server/Glue/GluedSystem.cs deleted file mode 100644 index 6a7516cd79..0000000000 --- a/Content.Server/Glue/GluedSystem.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Content.Shared.Interaction.Components; -using Robust.Shared.Timing; -using Content.Shared.Glue; -using Content.Shared.Hands; - -namespace Content.Server.Glue; - -public sealed class GluedSystem : EntitySystem -{ - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGluedInit); - SubscribeLocalEvent(OnHandPickUp); - } - - // Timing to remove glued and unremoveable. - public override void Update(float frameTime) - { - base.Update(frameTime); - - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var glue)) - { - if (!glue.GlueBroken || glue.Glued) - continue; - - if (_timing.CurTime < glue.GlueTime) - continue; - - glue.Glued = false; - glue.GlueBroken = false; - MetaData(uid).EntityName = glue.BeforeGluedEntityName; - RemComp(uid); - RemComp(uid); - } - } - - //Adds the prefix on init. - private void OnGluedInit(EntityUid uid, GluedComponent component, ComponentInit args) - { - var meta = MetaData(uid); - var name = meta.EntityName; - component.BeforeGluedEntityName = meta.EntityName; - meta.EntityName = Loc.GetString("glued-name-prefix", ("target", name)); - } - - // Timers start only when the glued item is picked up. - private void OnHandPickUp(EntityUid uid, GluedComponent component, GotEquippedHandEvent args) - { - EnsureComp(uid); - component.GlueBroken = true; - component.GlueTime = _timing.CurTime + component.GlueCooldown; - } -} diff --git a/Content.Shared/Glue/GlueComponent.cs b/Content.Shared/Glue/GlueComponent.cs index 89a2c39789..b9a5ff70ad 100644 --- a/Content.Shared/Glue/GlueComponent.cs +++ b/Content.Shared/Glue/GlueComponent.cs @@ -1,15 +1,42 @@ +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.Glue +namespace Content.Shared.Glue; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedGlueSystem))] +public sealed class GlueComponent : Component { - [RegisterComponent, NetworkedComponent] - public sealed class GlueComponent : Component - { - /// - /// Noise made when glue applied. - /// - [DataField("squeeze")] - public SoundSpecifier Squeeze = new SoundPathSpecifier("/Audio/Items/squeezebottle.ogg"); - } + /// + /// Noise made when glue applied. + /// + [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 = "SpaceGlue"; + + /// + /// Reagent consumption per use. + /// + [DataField("consumptionUnit"), ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 ConsumptionUnit = FixedPoint2.New(5); + + /// + /// Duration per unit + /// + [DataField("durationPerUnit"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan DurationPerUnit = TimeSpan.FromSeconds(6); } diff --git a/Content.Shared/Glue/GluedComponent.cs b/Content.Shared/Glue/GluedComponent.cs index e9fcead70f..9d34d9722e 100644 --- a/Content.Shared/Glue/GluedComponent.cs +++ b/Content.Shared/Glue/GluedComponent.cs @@ -1,42 +1,20 @@ -using Robust.Shared.Audio; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Glue; [RegisterComponent] +[Access(typeof(SharedGlueSystem))] public sealed class GluedComponent : Component { /// /// Reverts name to before prefix event (essentially removes prefix). /// [DataField("beforeGluedEntityName"), ViewVariables(VVAccess.ReadOnly)] - public string BeforeGluedEntityName = String.Empty; + public string BeforeGluedEntityName = string.Empty; - /// - /// Sound made when glue applied. - /// - [DataField("squeeze")] - public SoundSpecifier Squeeze = new SoundPathSpecifier("/Audio/Items/squeezebottle.ogg"); + [DataField("until", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Until; - /// - /// Timings for glue duration and removal. - /// - [DataField("nextGlueTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] - public TimeSpan? NextGlueTime; - - [DataField("glueTime", customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan GlueTime = TimeSpan.Zero; - - [DataField("glueCooldown")] - public TimeSpan GlueCooldown = TimeSpan.FromSeconds(20); - - - /// - /// Bools which control timings and when to apply the glue effect. - /// - public bool GlueBroken = false; - - [DataField("glued")] - [ViewVariables(VVAccess.ReadWrite)] - public bool Glued = false; + [DataField("duration", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Duration; } diff --git a/Content.Shared/Glue/SharedGlueSystem.cs b/Content.Shared/Glue/SharedGlueSystem.cs new file mode 100644 index 0000000000..d8b413a1e1 --- /dev/null +++ b/Content.Shared/Glue/SharedGlueSystem.cs @@ -0,0 +1,5 @@ +namespace Content.Shared.Glue; + +public abstract class SharedGlueSystem : EntitySystem +{ +} diff --git a/Resources/Locale/en-US/glue/glue.ftl b/Resources/Locale/en-US/glue/glue.ftl index 2f12083a14..1d0db515cb 100644 --- a/Resources/Locale/en-US/glue/glue.ftl +++ b/Resources/Locale/en-US/glue/glue.ftl @@ -1,3 +1,3 @@ -glue-success = {THE($target)} has been covered in glue. +glue-success = {THE($target)} has been covered in glue! glued-name-prefix = Glued {$target} -glue-failure = {THE($target)} is already covered in glue. +glue-failure = Can't cover {THE($target)} in glue! diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml index cfdc5a6b54..edbe6e6865 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml @@ -19,6 +19,7 @@ - state: icon-front map: [ "enum.SolutionContainerLayers.Overlay" ] - type: Appearance + - type: Glue - type: SolutionContainerManager solutions: drink: diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index e487a1651b..e6f1dc8a74 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1144,43 +1144,27 @@ Quantity: 100 - type: entity - parent: FoodProduceBase + parent: DrinkBase id: CrazyGlue name: crazy glue description: A bottle of crazy glue manufactured by Honk! Co. components: - - type: FlavorProfile - flavors: - - glue - - type: Food - trash: CrazyGlueEmpty + - type: Drink + isOpen: false + openSounds: + collection: packetOpenSounds - type: Sprite sprite: Objects/Fun/glue.rsi - state: icon-0 + state: icon - type: Appearance - type: Glue + consumptionUnit: 10 - type: Item sprite: Objects/Fun/glue.rsi - type: SolutionContainerManager solutions: - food: - maxVol: 15 + drink: + maxVol: 100 reagents: - ReagentId: SpaceGlue - Quantity: 6 - -- type: entity - name: empty crazy glue - parent: BaseItem - id: CrazyGlueEmpty - components: - - type: Sprite - sprite: Objects/Fun/glue.rsi - state: icon-1 - - type: Item - sprite: Objects/Fun/glue.rsi - - type: Tag - tags: - - Recyclable - - Trash - - type: SpaceGarbage + Quantity: 100 diff --git a/Resources/Textures/Objects/Fun/glue.rsi/icon-1.png b/Resources/Textures/Objects/Fun/glue.rsi/icon-1.png deleted file mode 100644 index b648e9eb40..0000000000 Binary files a/Resources/Textures/Objects/Fun/glue.rsi/icon-1.png and /dev/null differ diff --git a/Resources/Textures/Objects/Fun/glue.rsi/icon-0.png b/Resources/Textures/Objects/Fun/glue.rsi/icon.png similarity index 100% rename from Resources/Textures/Objects/Fun/glue.rsi/icon-0.png rename to Resources/Textures/Objects/Fun/glue.rsi/icon.png diff --git a/Resources/Textures/Objects/Fun/glue.rsi/meta.json b/Resources/Textures/Objects/Fun/glue.rsi/meta.json index be4241324e..d2ae18500a 100644 --- a/Resources/Textures/Objects/Fun/glue.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/glue.rsi/meta.json @@ -8,10 +8,7 @@ }, "states": [ { - "name": "icon-0" - }, - { - "name": "icon-1" + "name": "icon" }, { "name": "inhand-left",