#nullable enable using System; using System.Threading.Tasks; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.MachineLinking; using Content.Server.GameObjects.Components.MachineLinking.Signals; using Content.Server.GameObjects.Components.Mobs; using Content.Shared.Damage; using Content.Shared.GameObjects.Components.Damage; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.Container; 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.Interfaces.Timing; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers { /// /// Component that represents a wall light. It has a light bulb that can be replaced when broken. /// [RegisterComponent] public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver, ISignalReceiver { [Dependency] private readonly IGameTiming _gameTiming = default!; public override string Name => "PoweredLight"; private static readonly TimeSpan _thunkDelay = TimeSpan.FromSeconds(2); private TimeSpan _lastThunk; private bool _hasLampOnSpawn; [ViewVariables] private bool _on; private LightBulbType BulbType = LightBulbType.Tube; [ViewVariables] private ContainerSlot _lightBulbContainer = default!; [ViewVariables] private LightBulbComponent? LightBulb { get { if (_lightBulbContainer.ContainedEntity == null) return null; _lightBulbContainer.ContainedEntity.TryGetComponent(out LightBulbComponent? bulb); return bulb; } } // TODO CONSTRUCTION make this use a construction graph public async Task InteractUsing(InteractUsingEventArgs eventArgs) { return InsertBulb(eventArgs.Using); } public bool InteractHand(InteractHandEventArgs eventArgs) { if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent)) { Eject(); return false; } if(eventArgs.User.TryGetComponent(out HeatResistanceComponent? heatResistanceComponent)) { if(CanBurn(heatResistanceComponent.GetHeatResistance())) { Burn(); return true; } } Eject(); return true; bool CanBurn(int heatResistance) { if (LightBulb == null) return false; return _lightState && heatResistance < LightBulb.BurningTemperature; } void Burn() { Owner.PopupMessage(eventArgs.User, Loc.GetString("You burn your hand!")); damageableComponent.ChangeDamage(DamageType.Heat, 20, false, Owner); var audioSystem = EntitySystem.Get(); audioSystem.PlayFromEntity("/Audio/Effects/lightburn.ogg", Owner); } void Eject() { EjectBulb(eventArgs.User); UpdateLight(); } } /// /// Inserts the bulb if possible. /// /// True if it could insert it, false if it couldn't. private bool InsertBulb(IEntity bulb) { if (LightBulb != null) return false; if (!bulb.TryGetComponent(out LightBulbComponent? lightBulb)) return false; if (lightBulb.Type != BulbType) return false; var inserted = _lightBulbContainer.Insert(bulb); lightBulb.OnLightBulbStateChange += UpdateLight; lightBulb.OnLightColorChange += UpdateLight; UpdateLight(); return inserted; } /// /// Ejects the bulb to a mob's hand if possible. /// private void EjectBulb(IEntity user) { if (LightBulb == null) return; var bulb = LightBulb; bulb.OnLightBulbStateChange -= UpdateLight; bulb.OnLightColorChange -= UpdateLight; if (!_lightBulbContainer.Remove(bulb.Owner)) return; if (!user.TryGetComponent(out HandsComponent? hands) || !hands.PutInHand(bulb.Owner.GetComponent())) bulb.Owner.Transform.Coordinates = user.Transform.Coordinates; } public override void ExposeData(ObjectSerializer serializer) { serializer.DataField(ref BulbType, "bulb", LightBulbType.Tube); serializer.DataField(ref _on, "on", true); serializer.DataField(ref _hasLampOnSpawn, "hasLampOnSpawn", true); } /// /// For attaching UpdateLight() to events. /// public void UpdateLight(object? sender, EventArgs? e) { UpdateLight(); } private bool _lightState => Owner.GetComponent().Enabled; /// /// Updates the light's power drain, sprite and actual light state. /// public void UpdateLight() { var powerReceiver = Owner.GetComponent(); var sprite = Owner.GetComponent(); var light = Owner.GetComponent(); if (LightBulb == null) // No light bulb. { powerReceiver.Load = 0; sprite.LayerSetState(0, "empty"); light.Enabled = false; return; } switch (LightBulb.State) { case LightBulbState.Normal: if (powerReceiver.Powered && _on) { powerReceiver.Load = LightBulb.PowerUse; sprite.LayerSetState(0, "on"); light.Enabled = true; light.Color = LightBulb.Color; var time = _gameTiming.CurTime; if (time > _lastThunk + _thunkDelay) { _lastThunk = time; EntitySystem.Get().PlayFromEntity("/Audio/Machines/light_tube_on.ogg", Owner, AudioParams.Default.WithVolume(-10f)); } } else { sprite.LayerSetState(0, "off"); light.Enabled = false; } break; case LightBulbState.Broken: sprite.LayerSetState(0, "broken"); light.Enabled = false; break; case LightBulbState.Burned: sprite.LayerSetState(0, "burned"); light.Enabled = false; break; } } public override void Initialize() { base.Initialize(); _lightBulbContainer = ContainerManagerComponent.Ensure("light_bulb", Owner); } public override void HandleMessage(ComponentMessage message, IComponent? component) { base.HandleMessage(message, component); switch (message) { case PowerChangedMessage: UpdateLight(); break; } } void IMapInit.MapInit() { if (_hasLampOnSpawn) { var prototype = BulbType switch { LightBulbType.Bulb => "LightBulb", LightBulbType.Tube => "LightTube", _ => throw new ArgumentOutOfRangeException() }; var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates); _lightBulbContainer.Insert(entity); UpdateLight(); } } public void TriggerSignal(bool signal) { _on = signal; UpdateLight(); } public void TriggerSignal(ToggleSignal signal) { _on = !_on; UpdateLight(); } } }