using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Light.Components; using Content.Shared.Light.Events; using Content.Shared.Popups; using Content.Shared.Storage.EntitySystems; using Content.Shared.Storage.Events; using Robust.Shared.Audio.Systems; using Robust.Shared.Serialization; namespace Content.Shared.Light.EntitySystems; /// /// System that handles light replacer tool (which picks proper light entity for socket /// and replaces it with other one, hopefully working one!). /// public sealed partial class LightReplacerSystem : EntitySystem { [Dependency] private SharedAudioSystem _audio = default!; [Dependency] private EntityProviderSystem _provider = default!; [Dependency] private SharedPopupSystem _popup = default!; [Dependency] private SharedPoweredLightSystem _poweredLight = default!; [Dependency] private SharedUserInterfaceSystem _ui = default!; [Dependency] private EntityQuery _lightBulbQuery = default!; /// Adds contents info into examine. [SubscribeLocalEvent] private void OnExamined(Entity replacer, ref ExaminedEvent args) { if (!_provider.TryGetEntityCounter(replacer.Owner, out var entities)) return; using (args.PushGroup(nameof(LightReplacerComponent))) { if (entities.Count == 0) { args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights")); return; } args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights")); foreach (var bulb in entities) { if (!ProtoMan.Resolve(bulb.Key, out var bulbPrototype)) continue; args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", bulb.Value), ("name", bulbPrototype.Name))); } } } /// Attempts to open UI for replacer, if there are any viable options to put into it. [SubscribeLocalEvent] private void OnUse(Entity replacer, ref UseInHandEvent args) { if (args.Handled || !_provider.TryGetEntityCounter(replacer.Owner, out var entities)) return; args.ApplyDelay = false; if (entities.Count == 0) { _popup.PopupEntity(Loc.GetString("comp-light-replacer-open-empty", ("light-replacer", replacer)), replacer, args.User); return; } args.Handled = true; _ui.OpenUi(replacer.Owner, LightReplacerUiKey.Key, args.User); } /// Tries to replace bulb/tube if applicable. [SubscribeLocalEvent] private void OnAfterInteract(Entity replacer, ref AfterInteractEvent eventArgs) { if (eventArgs.Handled || !eventArgs.CanReach // standard interaction checks || eventArgs.Target == null) // behavior will depend on the target type return; var targetUid = (EntityUid) eventArgs.Target; // replace broken light in fixture? if (TryComp(targetUid, out var fixture)) eventArgs.Handled = TryReplaceBulb(replacer.AsNullable(), (targetUid, fixture), eventArgs.User); } [SubscribeLocalEvent] private void OnEjectMessage(Entity replacer, ref EjectLightTypeMessage args) { _provider.TryEjectEntities(replacer.Owner, args.LightEntProtoId, out _, user: args.Actor); } /// Attempts to switch currently selected bulb type according to request. [SubscribeLocalEvent] private void OnSwitchMessage(Entity replacer, ref SwitchLightTypeMessage args) { if (!ProtoMan.Resolve(args.LightEntProtoId, out var prototype) || !_provider.TryGetEntityCounter(replacer.Owner, out var entities) || !entities.TryGetValue(args.LightEntProtoId, out var amount) || amount <= 0) return; if (args.LightType == LightBulbType.Tube) replacer.Comp.ActiveLightTube = args.LightEntProtoId; else replacer.Comp.ActiveLightBulb = args.LightEntProtoId; Dirty(replacer); _audio.PlayPredicted(replacer.Comp.Sound, replacer, args.Actor); } [SubscribeLocalEvent] private void OnLightProviderInsertedCheck(Entity bulb, ref EntityProviderInsertCheckEvent args) { if (bulb.Comp.State == LightBulbState.Broken) args.FailureMessage = Loc.GetString("comp-light-replacer-insert-broken-light"); } /// /// Try to replace a light bulb in /// using light replacer. Light fixture should have . /// /// The light replacer used to replace the bulb. /// The fixture whose light is being replaced. /// The user who is replacing the light. /// True if successfully replaced light, false otherwise public bool TryReplaceBulb(Entity replacer, Entity lightHolder, EntityUid? userUid = null) { if (!Resolve(replacer, ref replacer.Comp) || !Resolve(lightHolder, ref lightHolder.Comp)) return false; var activeType = lightHolder.Comp.BulbType == LightBulbType.Tube ? replacer.Comp.ActiveLightTube : replacer.Comp.ActiveLightBulb; // check if light bulb is broken or missing EntityUid? currentBulbInHolder = _poweredLight.GetBulb(lightHolder, lightHolder.Comp); if (currentBulbInHolder != null) { if (!_lightBulbQuery.TryComp(currentBulbInHolder.Value, out var fixtureBulb)) return false; var prototype = MetaData(currentBulbInHolder.Value).EntityPrototype; if (fixtureBulb.State == LightBulbState.Normal && prototype != null && prototype.ID == activeType) { _popup.PopupEntity(Loc.GetString("comp-light-replacer-same-light", ("light", currentBulbInHolder)), lightHolder, userUid, PopupType.Medium); return false; } } if (!_provider.TryGetEntity(replacer.Owner, activeType, out var insertedBulb)) { if (userUid == null || !ProtoMan.Resolve(activeType, out var bulbPrototype)) return false; var msg = Loc.GetString("comp-light-replacer-missing-light", ("light-name", bulbPrototype.Name), ("light-replacer", replacer)); _popup.PopupEntity(msg, replacer, userUid.Value); return false; } // insert it into fixture var wasReplaced = _poweredLight.ReplaceBulb(lightHolder, insertedBulb.Value, lightHolder.Comp); if (wasReplaced) { _audio.PlayPredicted(replacer.Comp.Sound, replacer, userUid); } return wasReplaced; } } [Serializable, NetSerializable] public enum LightReplacerUiKey : byte { Key, }