#nullable enable using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.NodeContainer; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.PowerNetComponents; using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Power.AME; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Localization; using Robust.Shared.ViewVariables; using System.Linq; using System.Threading.Tasks; using Content.Shared.Interfaces; namespace Content.Server.GameObjects.Components.Power.AME { [RegisterComponent] [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IInteractUsing))] public class AMEControllerComponent : SharedAMEControllerComponent, IActivate, IInteractUsing { [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(AMEControllerUiKey.Key); [ViewVariables] private bool _injecting; [ViewVariables] public int InjectionAmount; private AppearanceComponent? _appearance; private PowerSupplierComponent? _powerSupplier; private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered; [ViewVariables] private int _stability = 100; private ContainerSlot _jarSlot = default!; [ViewVariables] private bool HasJar => _jarSlot.ContainedEntity != null; public override void Initialize() { base.Initialize(); if (UserInterface != null) { UserInterface.OnReceiveMessage += OnUiReceiveMessage; } Owner.TryGetComponent(out _appearance); if (Owner.TryGetComponent(out PowerReceiverComponent? receiver)) { receiver.OnPowerStateChanged += OnPowerChanged; } Owner.TryGetComponent(out _powerSupplier); _injecting = false; InjectionAmount = 2; _jarSlot = ContainerManagerComponent.Ensure($"{Name}-fuelJarContainer", Owner); } internal void OnUpdate(float frameTime) { if(!_injecting) { return; } var group = GetAMENodeGroup(); if (group == null) { return; } _jarSlot.ContainedEntity.TryGetComponent(out var fuelJar); if(fuelJar != null && _powerSupplier != null && fuelJar.FuelAmount > InjectionAmount) { _powerSupplier.SupplyRate = group.InjectFuel(InjectionAmount); fuelJar.FuelAmount -= InjectionAmount; InjectSound(); UpdateUserInterface(); } _stability = group.GetTotalStability(); UpdateDisplay(_stability); if(_stability <= 0) { group.ExplodeCores(); } } /// /// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible. /// /// Data relevant to the event such as the actor which triggered it. void IActivate.Activate(ActivateEventArgs args) { if (!args.User.TryGetComponent(out IActorComponent? actor)) { return; } if (!args.User.TryGetComponent(out IHandsComponent? hands)) { Owner.PopupMessage(args.User, Loc.GetString("You have no hands.")); return; } var activeHandEntity = hands.GetActiveHand?.Owner; if (activeHandEntity == null) { UserInterface?.Open(actor.playerSession); } } private void OnPowerChanged(object? sender, PowerStateEventArgs e) { UpdateUserInterface(); } private AMEControllerBoundUserInterfaceState GetUserInterfaceState() { var jar = _jarSlot.ContainedEntity; if (jar == null) { return new AMEControllerBoundUserInterfaceState(Powered, IsMasterController(), false, HasJar, 0, InjectionAmount, GetCoreCount()); } var jarcomponent = jar.GetComponent(); return new AMEControllerBoundUserInterfaceState(Powered, IsMasterController(), _injecting, HasJar, jarcomponent.FuelAmount, InjectionAmount, GetCoreCount()); } /// /// Checks whether the player entity is able to use the controller. /// /// The player entity. /// Returns true if the entity can use the controller, and false if it cannot. private bool PlayerCanUseController(IEntity playerEntity, bool needsPower = true) { //Need player entity to check if they are still able to use the dispenser if (playerEntity == null) return false; //Check if player can interact in their current state if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity)) return false; //Check if device is powered if (needsPower && !Powered) return false; return true; } private void UpdateUserInterface() { var state = GetUserInterfaceState(); UserInterface?.SetState(state); } /// /// Handles ui messages from the client. For things such as button presses /// which interact with the world and require server action. /// /// A user interface message from the client. private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) { if (obj.Session.AttachedEntity == null) { return; } var msg = (UiButtonPressedMessage) obj.Message; var needsPower = msg.Button switch { UiButton.Eject => false, _ => true, }; if (!PlayerCanUseController(obj.Session.AttachedEntity, needsPower)) return; switch (msg.Button) { case UiButton.Eject: TryEject(obj.Session.AttachedEntity); break; case UiButton.ToggleInjection: ToggleInjection(); break; case UiButton.IncreaseFuel: InjectionAmount += 2; break; case UiButton.DecreaseFuel: InjectionAmount = InjectionAmount > 0 ? InjectionAmount -= 2 : 0; break; case UiButton.RefreshParts: RefreshParts(); break; } GetAMENodeGroup()?.UpdateCoreVisuals(InjectionAmount, _injecting); UpdateUserInterface(); ClickSound(); } private void TryEject(IEntity user) { if (!HasJar || _injecting) return; var jar = _jarSlot.ContainedEntity; _jarSlot.Remove(_jarSlot.ContainedEntity); UpdateUserInterface(); if (!user.TryGetComponent(out var hands) || !jar.TryGetComponent(out var item)) return; if (hands.CanPutInHand(item)) hands.PutInHand(item); } private void ToggleInjection() { if (!_injecting) { _appearance?.SetData(AMEControllerVisuals.DisplayState, "on"); } else { _appearance?.SetData(AMEControllerVisuals.DisplayState, "off"); if (_powerSupplier != null) { _powerSupplier.SupplyRate = 0; } } _injecting = !_injecting; UpdateUserInterface(); } private void UpdateDisplay(int stability) { if(_appearance == null) { return; } _appearance.TryGetData(AMEControllerVisuals.DisplayState, out var state); var newState = "on"; if (stability < 50) { newState = "critical"; } if (stability < 10) { newState = "fuck"; } if (state != newState) { _appearance?.SetData(AMEControllerVisuals.DisplayState, newState); } } private void RefreshParts() { GetAMENodeGroup()?.RefreshAMENodes(this); UpdateUserInterface(); } private AMENodeGroup? GetAMENodeGroup() { Owner.TryGetComponent(out NodeContainerComponent? nodeContainer); var engineNodeGroup = nodeContainer?.Nodes .Select(node => node.NodeGroup) .OfType() .First(); return engineNodeGroup; } private bool IsMasterController() { if(GetAMENodeGroup()?.MasterController == this) { return true; } return false; } private int GetCoreCount() { var coreCount = 0; var group = GetAMENodeGroup(); if (group != null) { coreCount = group.CoreCount; } return coreCount; } private void ClickSound() { EntitySystem.Get().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f)); } private void InjectSound() { EntitySystem.Get().PlayFromEntity("/Audio/Effects/bang.ogg", Owner, AudioParams.Default.WithVolume(0f)); } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs args) { if (!args.User.TryGetComponent(out IHandsComponent? hands)) { Owner.PopupMessage(args.User, Loc.GetString("You have no hands.")); return true; } if (hands.GetActiveHand == null) { Owner.PopupMessage(args.User, Loc.GetString("You have nothing on your hand.")); return false; } var activeHandEntity = hands.GetActiveHand.Owner; if (activeHandEntity.TryGetComponent(out var fuelContainer)) { if (HasJar) { Owner.PopupMessage(args.User, Loc.GetString("The controller already has a jar loaded.")); } else { _jarSlot.Insert(activeHandEntity); Owner.PopupMessage(args.User, Loc.GetString("You insert the jar into the fuel slot.")); UpdateUserInterface(); } } else { Owner.PopupMessage(args.User, Loc.GetString("You can't put that in the controller...")); } return true; } } }