using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.VendingMachines; using Content.Shared.VendingMachines; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Timers; using Robust.Shared.Utility; using static Content.Shared.GameObjects.Components.SharedWiresComponent; namespace Content.Server.GameObjects.Components.VendingMachines { [RegisterComponent] [ComponentReference(typeof(IActivate))] public class VendingMachineComponent : SharedVendingMachineComponent, IActivate, IExamine, IBreakAct, IWires { #pragma warning disable 649 [Dependency] private readonly IRobustRandom _random; #pragma warning restore 649 private AppearanceComponent _appearance; private BoundUserInterface _userInterface; private PowerDeviceComponent _powerDevice; private bool _ejecting = false; private TimeSpan _animationDuration = TimeSpan.Zero; private string _packPrototypeId; private string _description; private string _spriteName; private bool Powered => _powerDevice.Powered; private bool _broken = false; public void Activate(ActivateEventArgs eventArgs) { if(!eventArgs.User.TryGetComponent(out IActorComponent actor)) { return; } var wires = Owner.GetComponent(); if (wires.IsPanelOpen) { wires.OpenInterface(actor.playerSession); } else { _userInterface.Open(actor.playerSession); } } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref _packPrototypeId, "pack", string.Empty); } private void InitializeFromPrototype() { if (string.IsNullOrEmpty(_packPrototypeId)) { return; } var prototypeManger = IoCManager.Resolve(); if (!prototypeManger.TryIndex(_packPrototypeId, out VendingMachineInventoryPrototype packPrototype)) { return; } Owner.Name = packPrototype.Name; _description = packPrototype.Description; _animationDuration = TimeSpan.FromSeconds(packPrototype.AnimationDuration); _spriteName = packPrototype.SpriteName; if (!string.IsNullOrEmpty(_spriteName)) { var spriteComponent = Owner.GetComponent(); const string vendingMachineRSIPath = "Buildings/VendingMachines/{0}.rsi"; spriteComponent.BaseRSIPath = string.Format(vendingMachineRSIPath, _spriteName); } var inventory = new List(); foreach(var (id, amount) in packPrototype.StartingInventory) { inventory.Add(new VendingMachineInventoryEntry(id, amount)); } Inventory = inventory; } public override void Initialize() { base.Initialize(); _appearance = Owner.GetComponent(); _userInterface = Owner.GetComponent() .GetBoundUserInterface(VendingMachineUiKey.Key); _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; _powerDevice = Owner.GetComponent(); _powerDevice.OnPowerStateChanged += UpdatePower; InitializeFromPrototype(); } public override void OnRemove() { _appearance = null; _powerDevice.OnPowerStateChanged -= UpdatePower; _powerDevice = null; base.OnRemove(); } private void UpdatePower(object sender, PowerStateEventArgs args) { var state = args.Powered ? VendingMachineVisualState.Normal : VendingMachineVisualState.Off; TrySetVisualState(state); } private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) { var message = serverMsg.Message; switch (message) { case VendingMachineEjectMessage msg: TryEject(msg.ID); break; case InventorySyncRequestMessage msg: _userInterface.SendMessage(new VendingMachineInventoryMessage(Inventory)); break; } } public void Examine(FormattedMessage message) { if(_description == null) { return; } message.AddText(_description); } private void TryEject(string id) { if (_ejecting || _broken) { return; } VendingMachineInventoryEntry entry = Inventory.Find(x => x.ID == id); if (entry == null) { FlickDenyAnimation(); return; } if (entry.Amount <= 0) { FlickDenyAnimation(); return; } _ejecting = true; entry.Amount--; _userInterface.SendMessage(new VendingMachineInventoryMessage(Inventory)); TrySetVisualState(VendingMachineVisualState.Eject); Timer.Spawn(_animationDuration, () => { _ejecting = false; TrySetVisualState(VendingMachineVisualState.Normal); Owner.EntityManager.SpawnEntity(id, Owner.Transform.GridPosition); }); } private void FlickDenyAnimation() { TrySetVisualState(VendingMachineVisualState.Deny); //TODO: This duration should be a distinct value specific to the deny animation Timer.Spawn(_animationDuration, () => { TrySetVisualState(VendingMachineVisualState.Normal); }); } private void TrySetVisualState(VendingMachineVisualState state) { var finalState = state; if (_broken) { finalState = VendingMachineVisualState.Broken; } else if (_ejecting) { finalState = VendingMachineVisualState.Eject; } else if (!Powered) { finalState = VendingMachineVisualState.Off; } _appearance.SetData(VendingMachineVisuals.VisualState, finalState); } public void OnBreak(BreakageEventArgs eventArgs) { _broken = true; TrySetVisualState(VendingMachineVisualState.Broken); } public enum Wires { /// /// Shoots a random item when pulsed. /// Shoot } void IWires.RegisterWires(WiresComponent.WiresBuilder builder) { builder.CreateWire(Wires.Shoot); } void IWires.WiresUpdate(WiresUpdateEventArgs args) { var identifier = (Wires) args.Identifier; if (identifier == Wires.Shoot && args.Action == WiresAction.Pulse) { EjectRandom(); } } /// /// Ejects a random item if present. /// private void EjectRandom() { var availableItems = Inventory.Where(x => x.Amount > 0).ToList(); if (availableItems.Count <= 0) { return; } TryEject(_random.Pick(availableItems).ID); } } public class WiresUpdateEventArgs : EventArgs { public readonly object Identifier; public readonly WiresAction Action; public WiresUpdateEventArgs(object identifier, WiresAction action) { Identifier = identifier; Action = action; } } public interface IWires { void RegisterWires(WiresComponent.WiresBuilder builder); void WiresUpdate(WiresUpdateEventArgs args); } }