using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Advertise.Components; using Content.Shared.Advertise.Systems; using Content.Shared.Emag.Components; using Content.Shared.Emp; using Content.Shared.VendingMachines.Components; using Robust.Shared.Audio; namespace Content.Shared.VendingMachines; public abstract partial class SharedVendingMachineSystem { [Dependency] private AccessReaderSystem _accessReader = default!; [Dependency] private SharedSpeakOnUIClosedSystem _speakOn = default!; private void OnInventoryEjectMessage(Entity entity, ref VendingMachineEjectMessage args) { if (!_receiver.IsPowered(entity.Owner) || Deleted(entity)) return; if (args.Actor is not { Valid: true } actor) return; AuthorizedVend(entity.Owner, actor, args.Type, args.ID, entity.Comp); } [SubscribeLocalEvent] private void OnEmpPulse(Entity ent, ref EmpPulseEvent args) { if (ent.Comp.Broken || !_receiver.IsPowered(ent.Owner)) return; if (!TryComp(ent.Owner, out var eject)) return; args.Affected = true; args.Disabled = true; eject.NextEmpEject = Timing.CurTime; } private void UpdateEjectState(Entity entity, TimeSpan curTime) { var eject = entity.Comp2; if (eject.EjectEnd is { } ejectEnd && curTime > ejectEnd) { eject.EjectEnd = null; Dirty(entity.Owner, eject); EjectItem((entity.Owner, entity.Comp1, eject)); UpdateUI((entity.Owner, entity.Comp1)); OnEjectStateChanged((entity.Owner, entity.Comp1), eject); } if (eject.DenyEnd is not { } denyEnd || curTime <= denyEnd) return; eject.DenyEnd = null; Dirty(entity.Owner, eject); OnEjectStateChanged((entity.Owner, entity.Comp1), eject); } protected virtual void EjectItem(Entity entity, bool forceEject = false) { } protected virtual void OnEjectStateChanged(Entity entity, VendingMachineEjectComponent? ejectComponent = null) { } protected virtual bool ShouldThrowVendItem(Entity entity) { return false; } /// /// Checks if the user is authorized to use this vending machine /// /// /// Entity trying to use the vending machine /// public bool IsAuthorized(EntityUid uid, EntityUid sender, VendingMachineComponent? vendComponent = null) { if (!Resolve(uid, ref vendComponent)) return false; if (!TryComp(uid, out var accessReader)) return true; if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp(uid)) return true; Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid, sender); Deny((uid, vendComponent), sender); return false; } protected VendingMachineInventoryEntry? GetEntry(EntityUid uid, string entryId, InventoryType type, VendingMachineComponent? component = null) { if (!Resolve(uid, ref component)) return null; if (type == InventoryType.Emagged && HasComp(uid)) return component.EmaggedInventory.GetValueOrDefault(entryId); if (type == InventoryType.Contraband && component.Contraband) return component.ContrabandInventory.GetValueOrDefault(entryId); return component.Inventory.GetValueOrDefault(entryId); } /// /// Tries to eject the provided item. Will do nothing if the vending machine is incapable of ejecting, already ejecting /// or the item doesn't exist in its inventory. /// /// /// The type of inventory the item is from /// The prototype ID of the item /// Whether the item should be thrown in a random direction after ejection /// /// /// public void TryEjectVendorItem( EntityUid uid, InventoryType type, string itemId, bool throwItem, EntityUid? user = null, VendingMachineComponent? vendComponent = null, VendingMachineEjectComponent? ejectComponent = null) { if (!Resolve(uid, ref vendComponent)) return; if (!Resolve(uid, ref ejectComponent)) return; if (ejectComponent.Ejecting || vendComponent.Broken || !_receiver.IsPowered(uid)) { return; } var entry = GetEntry(uid, itemId, type, vendComponent); if (entry == null) { Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid, uid); Deny((uid, vendComponent), ejectComponent: ejectComponent); return; } if (entry.Amount <= 0) { Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid, uid); Deny((uid, vendComponent), ejectComponent: ejectComponent); return; } // Start Ejecting and prevent users from ordering while anim playing ejectComponent.EjectEnd = Timing.CurTime + ejectComponent.EjectDelay; ejectComponent.NextItemToEject = entry.ID; ejectComponent.ThrowNextItem = throwItem; if (TryComp(uid, out SpeakOnUIClosedComponent? speakComponent)) _speakOn.TrySetFlag((uid, speakComponent)); entry.Amount--; Dirty(uid, vendComponent); Dirty(uid, ejectComponent); UpdateUI((uid, vendComponent)); OnEjectStateChanged((uid, vendComponent), ejectComponent); Audio.PlayPredicted(ejectComponent.SoundVend, uid, user); } public void Deny(Entity entity, EntityUid? user = null, VendingMachineEjectComponent? ejectComponent = null) { if (!Resolve(entity.Owner, ref entity.Comp)) return; if (!Resolve(entity.Owner, ref ejectComponent)) return; if (ejectComponent.Denying) return; ejectComponent.DenyEnd = Timing.CurTime + ejectComponent.DenyDelay; var audioParams = ejectComponent.SoundDeny?.Params ?? AudioParams.Default; audioParams = audioParams.AddVolume(-2f); Audio.PlayPredicted(ejectComponent.SoundDeny, entity.Owner, user, audioParams); OnEjectStateChanged(entity, ejectComponent); Dirty(entity.Owner, ejectComponent); } /// /// Checks whether the user is authorized to use the vending machine, then ejects the provided item if true /// /// /// Entity that is trying to use the vending machine /// The type of inventory the item is from /// The prototype ID of the item /// public void AuthorizedVend(EntityUid uid, EntityUid sender, InventoryType type, string itemId, VendingMachineComponent component) { if (!IsAuthorized(uid, sender, component)) return; if (!TryComp(uid, out var ejectComponent)) return; TryEjectVendorItem(uid, type, itemId, ShouldThrowVendItem((uid, ejectComponent)), sender, component, ejectComponent); } }