diff --git a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs index e8762c8203..3793cecd4f 100644 --- a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs +++ b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs @@ -1,6 +1,8 @@ using Content.Shared.Kitchen; using Content.Server.Kitchen.EntitySystems; +using Content.Shared.Construction.Prototypes; using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Kitchen.Components { @@ -13,13 +15,30 @@ namespace Content.Server.Kitchen.Components [Access(typeof(ReagentGrinderSystem)), RegisterComponent] public sealed class ReagentGrinderComponent : Component { - //YAML serialization vars - [DataField("storageMaxEntities"), ViewVariables(VVAccess.ReadWrite)] - public int StorageMaxEntities = 16; + [ViewVariables(VVAccess.ReadWrite)] + public int StorageMaxEntities = 6; + + [DataField("baseStorageMaxEntities"), ViewVariables(VVAccess.ReadWrite)] + public int BaseStorageMaxEntities = 4; + + [DataField("machinePartStorageMax", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartStorageMax = "MatterBin"; + + [DataField("storagePerPartRating")] + public int StoragePerPartRating = 4; [DataField("workTime"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds. + [ViewVariables(VVAccess.ReadWrite)] + public float WorkTimeMultiplier = 1; + + [DataField("machinePartWorkTime", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartWorkTime = "Manipulator"; + + [DataField("partRatingWorkTimeMultiplier")] + public float PartRatingWorkTimerMulitplier = 0.6f; + [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); @@ -28,6 +47,8 @@ namespace Content.Server.Kitchen.Components [DataField("juiceSound"), ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg"); + + public IPlayingAudioStream? AudioStream; } [Access(typeof(ReagentGrinderSystem)), RegisterComponent] @@ -37,7 +58,7 @@ namespace Content.Server.Kitchen.Components /// Remaining time until the grinder finishes grinding/juicing. /// [ViewVariables] - public float WorkTimer; + public TimeSpan EndTime; [ViewVariables] public GrinderProgram Program; diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index 1a90051471..c1ce59777d 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Chemistry.EntitySystems; +using Content.Server.Construction; using Content.Server.Kitchen.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -16,13 +17,14 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Player; -using Robust.Shared.Utility; +using Robust.Shared.Timing; namespace Content.Server.Kitchen.EntitySystems { [UsedImplicitly] internal sealed class ReagentGrinderSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -36,10 +38,11 @@ namespace Content.Server.Kitchen.EntitySystems { base.Initialize(); - SubscribeLocalEvent((uid, component, _) => UpdateUiState(uid, component)); - SubscribeLocalEvent( - (EntityUid uid, ReagentGrinderComponent component, ref PowerChangedEvent _) => UpdateUiState(uid, component)); + SubscribeLocalEvent((uid, _, _) => UpdateUiState(uid)); + SubscribeLocalEvent((EntityUid uid, ReagentGrinderComponent _, ref PowerChangedEvent _) => UpdateUiState(uid)); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnContainerModified); SubscribeLocalEvent(OnContainerModified); @@ -57,11 +60,11 @@ namespace Content.Server.Kitchen.EntitySystems foreach (var (active, reagentGrinder) in EntityQuery()) { var uid = reagentGrinder.Owner; - active.WorkTimer -= frameTime; - if (active.WorkTimer > 0) + if (active.EndTime > _timing.CurTime) continue; + reagentGrinder.AudioStream?.Stop(); RemCompDeferred(uid); var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId); @@ -110,7 +113,7 @@ namespace Content.Server.Kitchen.EntitySystems _userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key, new ReagentGrinderWorkCompleteMessage()); - UpdateUiState(uid, reagentGrinder); + UpdateUiState(uid); } } @@ -122,7 +125,7 @@ namespace Content.Server.Kitchen.EntitySystems private void OnContainerModified(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerModifiedMessage args) { - UpdateUiState(uid, reagentGrinder); + UpdateUiState(uid); var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId); _appearanceSystem.SetData(uid, ReagentGrinderVisualState.BeakerAttached, outputContainer.HasValue); @@ -161,7 +164,25 @@ namespace Content.Server.Kitchen.EntitySystems args.Handled = true; } - private void UpdateUiState(EntityUid uid, ReagentGrinderComponent reagentGrinder) + /// + /// Gotta be efficient, you know? you're saving a whole extra second here and everything. + /// + private void OnRefreshParts(EntityUid uid, ReagentGrinderComponent component, RefreshPartsEvent args) + { + var ratingWorkTime = args.PartRatings[component.MachinePartWorkTime]; + var ratingStorage = args.PartRatings[component.MachinePartStorageMax]; + + component.WorkTimeMultiplier = MathF.Pow(component.PartRatingWorkTimerMulitplier, ratingWorkTime - 1); + component.StorageMaxEntities = component.BaseStorageMaxEntities + (int) (component.StoragePerPartRating * (ratingStorage - 1)); + } + + private void OnUpgradeExamine(EntityUid uid, ReagentGrinderComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("reagent-grinder-component-upgrade-work-time", component.WorkTimeMultiplier); + args.AddNumberUpgrade("reagent-grinder-component-upgrade-storage", component.StorageMaxEntities - component.BaseStorageMaxEntities); + } + + private void UpdateUiState(EntityUid uid) { var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId); var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId); @@ -195,7 +216,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!this.IsPowered(uid, EntityManager) || HasComp(uid)) return; - DoWork(uid, reagentGrinder, message.Session.AttachedEntity, message.Program); + DoWork(uid, reagentGrinder, message.Program); } private void OnEjectChamberAllMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberAllMessage message) @@ -211,7 +232,7 @@ namespace Content.Server.Kitchen.EntitySystems inputContainer.Remove(entity); entity.RandomOffset(0.4f); } - UpdateUiState(uid, reagentGrinder); + UpdateUiState(uid); } private void OnEjectChamberContentMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberContentMessage message) @@ -225,15 +246,17 @@ namespace Content.Server.Kitchen.EntitySystems { message.EntityId.RandomOffset(0.4f); ClickSound(uid, reagentGrinder); - UpdateUiState(uid, reagentGrinder); + UpdateUiState(uid); } } /// /// The wzhzhzh of the grinder. Processes the contents of the grinder and puts the output in the beaker. /// + /// The grinder itself + /// /// Which program, such as grind or juice - private void DoWork(EntityUid uid, ReagentGrinderComponent reagentGrinder, EntityUid? user, GrinderProgram program) + private void DoWork(EntityUid uid, ReagentGrinderComponent reagentGrinder, GrinderProgram program) { var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId); var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId); @@ -242,7 +265,7 @@ namespace Content.Server.Kitchen.EntitySystems if (inputContainer.ContainedEntities.Count <= 0 || !HasComp(outputContainer)) return; - SoundSpecifier? sound = null; + SoundSpecifier? sound; switch (program) { case GrinderProgram.Grind when inputContainer.ContainedEntities.All(CanGrind): @@ -256,10 +279,11 @@ namespace Content.Server.Kitchen.EntitySystems } var active = AddComp(uid); - active.WorkTimer = (float) reagentGrinder.WorkTime.TotalSeconds; + active.EndTime = _timing.CurTime + reagentGrinder.WorkTime * reagentGrinder.WorkTimeMultiplier; active.Program = program; - _audioSystem.PlayPvs(sound, uid); + reagentGrinder.AudioStream = _audioSystem.PlayPvs(sound, uid, + AudioParams.Default.WithPitchScale(1 / reagentGrinder.WorkTimeMultiplier)); //slightly higher pitched _userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key, new ReagentGrinderWorkStartedMessage(program)); } diff --git a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl index 30af6e9872..ffff7c4e6f 100644 --- a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl @@ -4,6 +4,9 @@ reagent-grinder-bound-user-interface-instant-button = INSTANT reagent-grinder-bound-user-interface-cook-time-label = COOK TIME reagent-grinder-component-cannot-put-entity-message = You can't put this in the reagent grinder! +reagent-grinder-component-upgrade-work-time = Work time +reagent-grinder-component-upgrade-storage = Storage + grinder-menu-title = All-In-One Grinder 3000 grinder-menu-grind-button = Grind grinder-menu-juice-button = Juice diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 3dbb4be59a..664eb9f55c 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -441,9 +441,9 @@ - type: MachineBoard prototype: KitchenReagentGrinder requirements: - Laser: 1 - ScanningModule: 1 + Capacitor: 1 MatterBin: 2 + Manipulator: 2 tagRequirements: GlassBeaker: Amount: 1 diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index a2c1d0ad32..9859d83a3e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -35,6 +35,9 @@ snapCardinals: true - type: ApcPowerReceiver powerLoad: 300 + - type: UpgradePowerDraw + powerDrawMultiplier: 0.75 + scaling: Exponential - type: ItemSlots slots: beakerSlot: