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,
}