309 lines
12 KiB
C#
309 lines
12 KiB
C#
using System.Linq;
|
|
using Content.Shared._DV.Light.Events;
|
|
using Content.Shared.Examine;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Light.Components;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Storage;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Serialization;
|
|
|
|
namespace Content.Shared.Light.EntitySystems;
|
|
|
|
public sealed class SharedLightReplacerSystem : EntitySystem // There is no Client and Server System anymore, this has been greatly overhauled in Delta-V.
|
|
{
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
|
[Dependency] private readonly SharedPoweredLightSystem _poweredLight = default!;
|
|
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
|
|
|
private EntityQuery<LightBulbComponent> _lightBulbQuery;
|
|
private EntityQuery<MetaDataComponent> _metaDataQuery;
|
|
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<LightReplacerComponent, ComponentInit>(OnInit);
|
|
SubscribeLocalEvent<LightReplacerComponent, MapInitEvent>(OnMapInit);
|
|
SubscribeLocalEvent<LightReplacerComponent, ExaminedEvent>(OnExamined);
|
|
SubscribeLocalEvent<LightReplacerComponent, UseInHandEvent>(OnUse);
|
|
SubscribeLocalEvent<LightReplacerComponent, InteractUsingEvent>(HandleInteract);
|
|
SubscribeLocalEvent<LightReplacerComponent, AfterInteractEvent>(HandleAfterInteract);
|
|
SubscribeLocalEvent<LightReplacerComponent, EjectLightTypeMessage>(OnEjectMessage);
|
|
SubscribeLocalEvent<LightReplacerComponent, SwitchLightTypeMessage>(OnSwitchMessage);
|
|
|
|
_lightBulbQuery = GetEntityQuery<LightBulbComponent>();
|
|
_metaDataQuery = GetEntityQuery<MetaDataComponent>();
|
|
}
|
|
|
|
private void OnInit(Entity<LightReplacerComponent> replacer, ref ComponentInit args)
|
|
{
|
|
// This needs to be handled on CompInit because otherwise, it's empty on the client.
|
|
replacer.Comp.InsertedBulbs = _container.EnsureContainer<Container>(replacer, "light_replacer_storage");
|
|
}
|
|
|
|
private void OnMapInit(Entity<LightReplacerComponent> replacer, ref MapInitEvent args)
|
|
{
|
|
var xform = Transform(replacer);
|
|
foreach (var spawn in EntitySpawnCollection.GetSpawns(replacer.Comp.Contents))
|
|
{
|
|
var light = Spawn(spawn, xform.Coordinates);
|
|
TryInsertBulb(replacer.AsNullable(), light);
|
|
}
|
|
}
|
|
|
|
private void OnExamined(Entity<LightReplacerComponent> replacer, ref ExaminedEvent args)
|
|
{
|
|
using (args.PushGroup(nameof(LightReplacerComponent)))
|
|
{
|
|
if (!replacer.Comp.InsertedBulbs.ContainedEntities.Any())
|
|
{
|
|
args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
|
|
return;
|
|
}
|
|
|
|
args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
|
|
var groups = new Dictionary<string, int>();
|
|
foreach (var bulb in replacer.Comp.InsertedBulbs.ContainedEntities)
|
|
{
|
|
var metaData = _metaDataQuery.GetComponent(bulb);
|
|
groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1;
|
|
}
|
|
|
|
foreach (var (name, amount) in groups)
|
|
{
|
|
args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing-dv", ("amount", amount), ("light-name", name)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnUse(Entity<LightReplacerComponent> replacer, ref UseInHandEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
args.ApplyDelay = false;
|
|
|
|
if (!replacer.Comp.InsertedBulbs.ContainedEntities.Any())
|
|
{
|
|
_popup.PopupClient(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);
|
|
}
|
|
|
|
private void HandleAfterInteract(Entity<LightReplacerComponent> 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<PoweredLightComponent>(targetUid, out var fixture))
|
|
eventArgs.Handled = TryReplaceBulb(replacer.AsNullable(), (targetUid, fixture), eventArgs.User);
|
|
// add new bulb to light replacer container?
|
|
else if (_lightBulbQuery.TryComp(targetUid, out var bulb))
|
|
eventArgs.Handled = TryInsertBulb(replacer.AsNullable(), (targetUid, bulb), eventArgs.User, true);
|
|
}
|
|
|
|
private void HandleInteract(Entity<LightReplacerComponent> replacer, ref InteractUsingEvent eventArgs)
|
|
{
|
|
if (eventArgs.Handled)
|
|
return;
|
|
|
|
var usedUid = eventArgs.Used;
|
|
|
|
// want to insert a new light bulb?
|
|
if (_lightBulbQuery.TryComp(usedUid, out var bulb))
|
|
eventArgs.Handled = TryInsertBulb(replacer.AsNullable(), (usedUid, bulb), eventArgs.User, true);
|
|
// add bulbs from storage?
|
|
else if (TryComp<StorageComponent>(usedUid, out var storage))
|
|
eventArgs.Handled = TryInsertBulbsFromStorage(replacer.AsNullable(), (usedUid, storage), eventArgs.User);
|
|
}
|
|
|
|
private void OnEjectMessage(Entity<LightReplacerComponent> replacer, ref EjectLightTypeMessage args)
|
|
{
|
|
HashSet<EntityUid> lightsToEject = [];
|
|
foreach (var light in replacer.Comp.InsertedBulbs.ContainedEntities)
|
|
{
|
|
if (_metaDataQuery.TryComp(light, out var metaData) && metaData.EntityName == args.LightName)
|
|
lightsToEject.Add(light);
|
|
}
|
|
|
|
foreach (var light in lightsToEject)
|
|
{
|
|
_container.Remove(light, replacer.Comp.InsertedBulbs);
|
|
}
|
|
}
|
|
|
|
private void OnSwitchMessage(Entity<LightReplacerComponent> replacer, ref SwitchLightTypeMessage args)
|
|
{
|
|
if (args.LightType == LightBulbType.Tube)
|
|
replacer.Comp.ActiveLightTube = args.LightName;
|
|
else
|
|
replacer.Comp.ActiveLightBulb = args.LightName;
|
|
Dirty(replacer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to replace a light bulb in <paramref name="fixture"/>
|
|
/// using light replacer. Light fixture should have <see cref="PoweredLightComponent"/>.
|
|
/// </summary>
|
|
/// <param name="replacer">The light replacer used to replace the bulb.</param>
|
|
/// <param name="fixture">The fixture whose light is being replaced.</param>
|
|
/// <param name="userUid">The user who is replacing the light.</param>
|
|
/// <returns>True if successfully replaced light, false otherwise</returns>
|
|
public bool TryReplaceBulb(Entity<LightReplacerComponent?> replacer, Entity<PoweredLightComponent?> fixture, EntityUid? userUid = null)
|
|
{
|
|
if (!Resolve(replacer, ref replacer.Comp)
|
|
|| !Resolve(fixture, ref fixture.Comp))
|
|
return false;
|
|
|
|
var activeType = fixture.Comp.BulbType == LightBulbType.Tube
|
|
? replacer.Comp.ActiveLightTube
|
|
: replacer.Comp.ActiveLightBulb;
|
|
|
|
// check if light bulb is broken or missing
|
|
var fixtureBulbUid = _poweredLight.GetBulb(fixture, fixture.Comp);
|
|
if (fixtureBulbUid != null)
|
|
{
|
|
if (!_lightBulbQuery.TryComp(fixtureBulbUid.Value, out var fixtureBulb))
|
|
return false;
|
|
|
|
if (fixtureBulb.State == LightBulbState.Normal
|
|
&& _metaDataQuery.TryComp(fixtureBulbUid, out var metaData)
|
|
&& metaData.EntityName == activeType)
|
|
{
|
|
_popup.PopupClient(Loc.GetString("comp-light-replacer-same-light", ("light-name", fixtureBulbUid)), fixture, userUid);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
EntityUid? bulb = null;
|
|
foreach (var insertedBulb in replacer.Comp.InsertedBulbs.ContainedEntities)
|
|
{
|
|
if (!_metaDataQuery.TryComp(insertedBulb, out var metaData) || metaData.EntityName != activeType)
|
|
continue;
|
|
|
|
bulb = insertedBulb;
|
|
break;
|
|
}
|
|
|
|
// found bulb in inserted storage
|
|
if (bulb.HasValue)
|
|
{
|
|
// try to remove it
|
|
var hasRemoved = _container.Remove(bulb.Value, replacer.Comp.InsertedBulbs);
|
|
if (!hasRemoved)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (userUid == null)
|
|
return false;
|
|
|
|
var msg = Loc.GetString("comp-light-replacer-missing-light-dv",
|
|
("light-name", activeType),
|
|
("light-replacer", replacer));
|
|
_popup.PopupClient(msg, replacer, userUid.Value);
|
|
return false;
|
|
}
|
|
|
|
// insert it into fixture
|
|
var wasReplaced = _poweredLight.ReplaceBulb(fixture, bulb.Value, fixture.Comp);
|
|
if (wasReplaced)
|
|
{
|
|
_audio.PlayPredicted(replacer.Comp.Sound, replacer, userUid);
|
|
}
|
|
|
|
return wasReplaced;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to insert a new bulb inside light replacer
|
|
/// </summary>
|
|
/// <param name="replacer">The light replacer to insert a light into.</param>
|
|
/// <param name="bulb">The light to insert into the replacer.</param>
|
|
/// <param name="userUid">The user who is inserting the light.</param>
|
|
/// <param name="showPopup">Whether to show a popup.</param>
|
|
/// <returns>True if successfully inserted light, false otherwise</returns>
|
|
public bool TryInsertBulb(Entity<LightReplacerComponent?> replacer, Entity<LightBulbComponent?> bulb, EntityUid? userUid = null, bool showPopup = false)
|
|
{
|
|
if (!Resolve(replacer, ref replacer.Comp)
|
|
|| !Resolve(bulb, ref bulb.Comp))
|
|
return false;
|
|
|
|
// only normal (non-broken) bulbs can be inserted inside light replacer
|
|
if (bulb.Comp.State != LightBulbState.Normal)
|
|
{
|
|
if (!showPopup || userUid == null)
|
|
return false;
|
|
|
|
var error = Loc.GetString("comp-light-replacer-insert-broken-light");
|
|
_popup.PopupClient(error, replacer, userUid.Value);
|
|
return false;
|
|
}
|
|
// try insert light and show message
|
|
var hasInsert = _container.Insert(bulb.Owner, replacer.Comp.InsertedBulbs);
|
|
|
|
if (!hasInsert || !showPopup || userUid == null)
|
|
return hasInsert;
|
|
|
|
var message = Loc.GetString("comp-light-replacer-insert-light",
|
|
("light-replacer", replacer), ("bulb", bulb));
|
|
_popup.PopupClient(message, replacer, userUid.Value);
|
|
|
|
return hasInsert;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to insert all light bulbs from storage (for example light tubes box)
|
|
/// </summary>
|
|
/// <param name="replacer">The light replacer to insert bulbs into.</param>
|
|
/// <param name="storage">The storage whose contents should be inserted.</param>
|
|
/// <param name="userUid">The user who inserts the contents.</param>
|
|
/// <returns>
|
|
/// Returns true if storage contained at least one light bulb
|
|
/// which was successfully inserted inside light replacer
|
|
/// </returns>
|
|
public bool TryInsertBulbsFromStorage(Entity<LightReplacerComponent?> replacer, Entity<StorageComponent?> storage, EntityUid? userUid = null)
|
|
{
|
|
if (!Resolve(replacer, ref replacer.Comp)
|
|
|| !Resolve(storage, ref storage.Comp))
|
|
return false;
|
|
|
|
var insertedBulbs = 0;
|
|
var storedEntities = storage.Comp.Container.ContainedEntities.ToArray();
|
|
|
|
foreach (var ent in storedEntities)
|
|
{
|
|
if (TryInsertBulb(replacer, ent, userUid))
|
|
{
|
|
insertedBulbs++;
|
|
}
|
|
}
|
|
|
|
// show some message if success
|
|
if (insertedBulbs > 0 && userUid != null)
|
|
{
|
|
var msg = Loc.GetString("comp-light-replacer-refill-from-storage", ("light-replacer", replacer));
|
|
_popup.PopupClient(msg, replacer, userUid.Value);
|
|
}
|
|
|
|
return insertedBulbs > 0;
|
|
}
|
|
}
|
|
|
|
[Serializable, NetSerializable]
|
|
public enum LightReplacerUiKey : byte
|
|
{
|
|
Key,
|
|
}
|