diff --git a/Content.Server/Light/Components/LightReplacerComponent.cs b/Content.Server/Light/Components/LightReplacerComponent.cs index 8835fb0c84..49c4523b86 100644 --- a/Content.Server/Light/Components/LightReplacerComponent.cs +++ b/Content.Server/Light/Components/LightReplacerComponent.cs @@ -1,45 +1,35 @@ -using Content.Shared.Light.Component; +using Content.Server.Light.EntitySystems; +using Content.Shared.Storage; using Robust.Shared.Audio; using Robust.Shared.Containers; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Light.Components +namespace Content.Server.Light.Components; + +/// +/// Device that allows user to quikly change bulbs in +/// Can be reloaded by new light tubes or light bulbs +/// +[RegisterComponent, Access(typeof(LightReplacerSystem))] +public sealed class LightReplacerComponent : Component { - /// - /// Device that allows user to quikly change bulbs in - /// Can be reloaded by new light tubes or light bulbs - /// - [RegisterComponent] - public sealed class LightReplacerComponent : Component + [DataField("sound")] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Weapons/click.ogg") { - [DataField("sound")] - public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Weapons/click.ogg"); - - /// - /// Bulbs that were inside light replacer when it spawned - /// - [DataField("contents")] - public List Contents = new(); - - /// - /// Bulbs that were inserted inside light replacer - /// - [ViewVariables] - public IContainer InsertedBulbs = default!; - - [Serializable] - [DataDefinition] - public sealed class LightReplacerEntity + Params = new() { - [DataField("name", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PrototypeName = default!; - - [DataField("amount")] - public int Amount; - - [DataField("type")] - public LightBulbType Type; + Volume = -4f } - } + }; + + /// + /// Bulbs that were inserted inside light replacer + /// + [ViewVariables] + public Container InsertedBulbs = default!; + + /// + /// The default starting bulbs + /// + [DataField("contents")] + public List Contents = new(); } diff --git a/Content.Server/Light/EntitySystems/LightReplacerSystem.cs b/Content.Server/Light/EntitySystems/LightReplacerSystem.cs index 2f7a6d07a5..e43ce5a5ae 100644 --- a/Content.Server/Light/EntitySystems/LightReplacerSystem.cs +++ b/Content.Server/Light/EntitySystems/LightReplacerSystem.cs @@ -1,217 +1,236 @@ using System.Linq; using Content.Server.Light.Components; using Content.Server.Storage.Components; +using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Light.Component; using Content.Shared.Popups; +using Content.Shared.Storage; using JetBrains.Annotations; -using Robust.Shared.Audio; using Robust.Shared.Containers; -using Robust.Shared.Player; -namespace Content.Server.Light.EntitySystems +namespace Content.Server.Light.EntitySystems; + +[UsedImplicitly] +public sealed class LightReplacerSystem : EntitySystem { - [UsedImplicitly] - public sealed class LightReplacerSystem : EntitySystem + [Dependency] private readonly PoweredLightSystem _poweredLight = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + + public override void Initialize() { - [Dependency] private readonly PoweredLightSystem _poweredLight = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + base.Initialize(); - public override void Initialize() + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(HandleInteract); + SubscribeLocalEvent(HandleAfterInteract); + } + + private void OnExamined(EntityUid uid, LightReplacerComponent component, ExaminedEvent args) + { + if (!component.InsertedBulbs.ContainedEntities.Any()) { - base.Initialize(); - - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(HandleInteract); - SubscribeLocalEvent(HandleAfterInteract); + args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights")); + return; + } + args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights")); + var groups = new Dictionary(); + var metaQuery = GetEntityQuery(); + foreach (var bulb in component.InsertedBulbs.ContainedEntities) + { + var metaData = metaQuery.GetComponent(bulb); + groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1; } - private void OnInit(EntityUid uid, LightReplacerComponent replacer, ComponentInit args) + foreach (var (name, amount) in groups) { - replacer.InsertedBulbs = ContainerHelpers.EnsureContainer(replacer.Owner, "light_replacer_storage"); - } - - private void HandleAfterInteract(EntityUid uid, LightReplacerComponent component, AfterInteractEvent eventArgs) - { - if (eventArgs.Handled) - return; - - // standard interaction checks - if (!eventArgs.CanReach) return; - - // behaviour will depends on target type - if (eventArgs.Target != null) - { - var targetUid = (EntityUid) eventArgs.Target; - - // replace broken light in fixture? - if (EntityManager.TryGetComponent(targetUid, out PoweredLightComponent? fixture)) - eventArgs.Handled = TryReplaceBulb(uid, targetUid, eventArgs.User, component, fixture); - // add new bulb to light replacer container? - else if (EntityManager.TryGetComponent(targetUid, out LightBulbComponent? bulb)) - eventArgs.Handled = TryInsertBulb(uid, targetUid, eventArgs.User, true, component, bulb); - } - } - - private void HandleInteract(EntityUid uid, LightReplacerComponent component, InteractUsingEvent eventArgs) - { - if (eventArgs.Handled) - return; - - var usedUid = eventArgs.Used; - - // want to insert a new light bulb? - if (EntityManager.TryGetComponent(usedUid, out LightBulbComponent? bulb)) - eventArgs.Handled = TryInsertBulb(uid, usedUid, eventArgs.User, true, component, bulb); - // add bulbs from storage? - else if (EntityManager.TryGetComponent(usedUid, out ServerStorageComponent? storage)) - eventArgs.Handled = TryInsertBulbsFromStorage(uid, usedUid, eventArgs.User, component, storage); - } - - /// - /// Try to replace a light bulb in - /// using light replacer. Light fixture should have . - /// - /// True if successfully replaced light, false otherwise - public bool TryReplaceBulb(EntityUid replacerUid, EntityUid fixtureUid, EntityUid? userUid = null, - LightReplacerComponent? replacer = null, PoweredLightComponent? fixture = null) - { - if (!Resolve(replacerUid, ref replacer)) - return false; - if (!Resolve(fixtureUid, ref fixture)) - return false; - - // check if light bulb is broken or missing - var fixtureBulbUid = _poweredLight.GetBulb(fixture.Owner, fixture); - if (fixtureBulbUid != null) - { - if (!EntityManager.TryGetComponent(fixtureBulbUid.Value, out LightBulbComponent? fixtureBulb)) - return false; - if (fixtureBulb.State == LightBulbState.Normal) - return false; - } - - // try get first inserted bulb of the same type as targeted light fixtutre - var bulb = replacer.InsertedBulbs.ContainedEntities.FirstOrDefault( - (e) => EntityManager.GetComponentOrNull(e)?.Type == fixture.BulbType); - - // found bulb in inserted storage - if (bulb.Valid) // FirstOrDefault can return default/invalid uid. - { - // try to remove it - var hasRemoved = replacer.InsertedBulbs.Remove(bulb); - if (!hasRemoved) - return false; - } - // try to create new instance of bulb from LightReplacerEntity - else - { - var bulbEnt = replacer.Contents.FirstOrDefault((e) => e.Type == fixture.BulbType && e.Amount > 0); - - // found right bulb, let's spawn it - if (bulbEnt != null) - { - bulb = EntityManager.SpawnEntity(bulbEnt.PrototypeName, EntityManager.GetComponent(replacer.Owner).Coordinates); - bulbEnt.Amount--; - } - // not found any light bulbs - else - { - if (userUid != null) - { - var msg = Loc.GetString("comp-light-replacer-missing-light", - ("light-replacer", replacer.Owner)); - _popupSystem.PopupEntity(msg, replacerUid, userUid.Value); - } - return false; - } - } - - // insert it into fixture - var wasReplaced = _poweredLight.ReplaceBulb(fixtureUid, bulb, fixture); - if (wasReplaced) - { - SoundSystem.Play(replacer.Sound.GetSound(), - Filter.Pvs(replacerUid), replacerUid, AudioParams.Default.WithVolume(-4f)); - } - - - return wasReplaced; - } - - /// - /// Try to insert a new bulb inside light replacer - /// - /// True if successfully inserted light, false otherwise - public bool TryInsertBulb(EntityUid replacerUid, EntityUid bulbUid, EntityUid? userUid = null, bool showTooltip = false, - LightReplacerComponent? replacer = null, LightBulbComponent? bulb = null) - { - if (!Resolve(replacerUid, ref replacer)) - return false; - if (!Resolve(bulbUid, ref bulb)) - return false; - - // only normal (non-broken) bulbs can be inserted inside light replacer - if (bulb.State != LightBulbState.Normal) - { - if (showTooltip && userUid != null) - { - var msg = Loc.GetString("comp-light-replacer-insert-broken-light"); - _popupSystem.PopupEntity(msg, replacerUid, userUid.Value); - } - - return false; - } - - // try insert light and show message - var hasInsert = replacer.InsertedBulbs.Insert(bulb.Owner); - if (hasInsert && showTooltip && userUid != null) - { - var msg = Loc.GetString("comp-light-replacer-insert-light", - ("light-replacer", replacer.Owner), ("bulb", bulb.Owner)); - _popupSystem.PopupEntity(msg, replacerUid, userUid.Value, PopupType.Medium); - } - - return hasInsert; - } - - /// - /// Try to insert all light bulbs from storage (for example light tubes box) - /// - /// - /// Returns true if storage contained at least one light bulb - /// which was successfully inserted inside light replacer - /// - public bool TryInsertBulbsFromStorage(EntityUid replacerUid, EntityUid storageUid, EntityUid? userUid = null, - LightReplacerComponent? replacer = null, ServerStorageComponent? storage = null) - { - if (!Resolve(replacerUid, ref replacer)) - return false; - if (!Resolve(storageUid, ref storage)) - return false; - - if (storage.StoredEntities == null) - return false; - - var insertedBulbs = 0; - var storagedEnts = storage.StoredEntities.ToArray(); - foreach (var ent in storagedEnts) - { - if (EntityManager.TryGetComponent(ent, out LightBulbComponent? bulb)) - { - if (TryInsertBulb(replacerUid, ent, userUid, false, replacer, bulb)) - insertedBulbs++; - } - } - - // show some message if success - if (insertedBulbs > 0 && userUid != null) - { - var msg = Loc.GetString("comp-light-replacer-refill-from-storage", ("light-replacer", storage.Owner)); - _popupSystem.PopupEntity(msg, replacerUid, userUid.Value, PopupType.Medium); - } - - return insertedBulbs > 0; + args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name))); } } + + private void OnMapInit(EntityUid uid, LightReplacerComponent component, MapInitEvent args) + { + var xform = Transform(uid); + foreach (var spawn in EntitySpawnCollection.GetSpawns(component.Contents)) + { + var ent = Spawn(spawn, xform.Coordinates); + TryInsertBulb(uid, ent, replacer: component); + } + } + + private void OnInit(EntityUid uid, LightReplacerComponent replacer, ComponentInit args) + { + replacer.InsertedBulbs = _container.EnsureContainer(uid, "light_replacer_storage"); + } + + private void HandleAfterInteract(EntityUid uid, LightReplacerComponent component, AfterInteractEvent eventArgs) + { + if (eventArgs.Handled) + return; + + // standard interaction checks + if (!eventArgs.CanReach) + return; + + // behaviour will depends on target type + if (eventArgs.Target != null) + { + var targetUid = (EntityUid) eventArgs.Target; + + // replace broken light in fixture? + if (TryComp(targetUid, out var fixture)) + eventArgs.Handled = TryReplaceBulb(uid, targetUid, eventArgs.User, component, fixture); + // add new bulb to light replacer container? + else if (TryComp(targetUid, out var bulb)) + eventArgs.Handled = TryInsertBulb(uid, targetUid, eventArgs.User, true, component, bulb); + } + } + + private void HandleInteract(EntityUid uid, LightReplacerComponent component, InteractUsingEvent eventArgs) + { + if (eventArgs.Handled) + return; + + var usedUid = eventArgs.Used; + + // want to insert a new light bulb? + if (TryComp(usedUid, out var bulb)) + eventArgs.Handled = TryInsertBulb(uid, usedUid, eventArgs.User, true, component, bulb); + // add bulbs from storage? + else if (TryComp(usedUid, out var storage)) + eventArgs.Handled = TryInsertBulbsFromStorage(uid, usedUid, eventArgs.User, component, storage); + } + + /// + /// Try to replace a light bulb in + /// using light replacer. Light fixture should have . + /// + /// True if successfully replaced light, false otherwise + public bool TryReplaceBulb(EntityUid replacerUid, EntityUid fixtureUid, EntityUid? userUid = null, + LightReplacerComponent? replacer = null, PoweredLightComponent? fixture = null) + { + if (!Resolve(replacerUid, ref replacer)) + return false; + if (!Resolve(fixtureUid, ref fixture)) + return false; + + // check if light bulb is broken or missing + var fixtureBulbUid = _poweredLight.GetBulb(fixtureUid, fixture); + if (fixtureBulbUid != null) + { + if (!TryComp(fixtureBulbUid.Value, out var fixtureBulb)) + return false; + if (fixtureBulb.State == LightBulbState.Normal) + return false; + } + + // try get first inserted bulb of the same type as targeted light fixtutre + var bulb = replacer.InsertedBulbs.ContainedEntities.FirstOrDefault( + e => CompOrNull(e)?.Type == fixture.BulbType); + + // found bulb in inserted storage + if (bulb.Valid) // FirstOrDefault can return default/invalid uid. + { + // try to remove it + var hasRemoved = replacer.InsertedBulbs.Remove(bulb); + if (!hasRemoved) + return false; + } + else + { + if (userUid != null) + { + var msg = Loc.GetString("comp-light-replacer-missing-light", + ("light-replacer", replacerUid)); + _popupSystem.PopupEntity(msg, replacerUid, userUid.Value); + } + return false; + } + + // insert it into fixture + var wasReplaced = _poweredLight.ReplaceBulb(fixtureUid, bulb, fixture); + if (wasReplaced) + { + _audio.PlayPvs(replacer.Sound, replacerUid); + } + + return wasReplaced; + } + + /// + /// Try to insert a new bulb inside light replacer + /// + /// True if successfully inserted light, false otherwise + public bool TryInsertBulb(EntityUid replacerUid, EntityUid bulbUid, EntityUid? userUid = null, bool showTooltip = false, + LightReplacerComponent? replacer = null, LightBulbComponent? bulb = null) + { + if (!Resolve(replacerUid, ref replacer)) + return false; + if (!Resolve(bulbUid, ref bulb)) + return false; + + // only normal (non-broken) bulbs can be inserted inside light replacer + if (bulb.State != LightBulbState.Normal) + { + if (showTooltip && userUid != null) + { + var msg = Loc.GetString("comp-light-replacer-insert-broken-light"); + _popupSystem.PopupEntity(msg, replacerUid, userUid.Value); + } + + return false; + } + + // try insert light and show message + var hasInsert = replacer.InsertedBulbs.Insert(bulbUid); + if (hasInsert && showTooltip && userUid != null) + { + var msg = Loc.GetString("comp-light-replacer-insert-light", + ("light-replacer", replacerUid), ("bulb", bulbUid)); + _popupSystem.PopupEntity(msg, replacerUid, userUid.Value, PopupType.Medium); + } + + return hasInsert; + } + + /// + /// Try to insert all light bulbs from storage (for example light tubes box) + /// + /// + /// Returns true if storage contained at least one light bulb + /// which was successfully inserted inside light replacer + /// + public bool TryInsertBulbsFromStorage(EntityUid replacerUid, EntityUid storageUid, EntityUid? userUid = null, + LightReplacerComponent? replacer = null, ServerStorageComponent? storage = null) + { + if (!Resolve(replacerUid, ref replacer)) + return false; + if (!Resolve(storageUid, ref storage)) + return false; + + if (storage.StoredEntities == null) + return false; + + var insertedBulbs = 0; + var storagedEnts = storage.StoredEntities.ToArray(); + foreach (var ent in storagedEnts) + { + if (TryComp(ent, out var bulb) && + TryInsertBulb(replacerUid, ent, userUid, false, replacer, bulb)) + insertedBulbs++; + } + + // show some message if success + if (insertedBulbs > 0 && userUid != null) + { + var msg = Loc.GetString("comp-light-replacer-refill-from-storage", ("light-replacer", storageUid)); + _popupSystem.PopupEntity(msg, replacerUid, userUid.Value, PopupType.Medium); + } + + return insertedBulbs > 0; + } } diff --git a/Resources/Locale/en-US/light/components/light-replacer-component.ftl b/Resources/Locale/en-US/light/components/light-replacer-component.ftl index f183ac8f7e..0cbb287a67 100644 --- a/Resources/Locale/en-US/light/components/light-replacer-component.ftl +++ b/Resources/Locale/en-US/light/components/light-replacer-component.ftl @@ -1,14 +1,23 @@ ### Interaction Messages -# Shown when player tries to replace light, but there is no lighs left -comp-light-replacer-missing-light = No lights left in {$light-replacer}. +# Shown when player tries to replace light, but there is no lights left +comp-light-replacer-missing-light = No lights left in {THE($light-replacer)}. # Shown when player inserts light bulb inside light replacer -comp-light-replacer-insert-light = You insert {$bulb} into {$light-replacer}. +comp-light-replacer-insert-light = You insert {$bulb} into {THE($light-replacer)}. # Shown when player tries to insert in light replacer brolen light bulb comp-light-replacer-insert-broken-light = You can't insert broken lights! # Shown when player refill light from light box -comp-light-replacer-refill-from-storage = You refill {$light-replacer}. \ No newline at end of file +comp-light-replacer-refill-from-storage = You refill {THE($light-replacer)}. + +### Examine + +comp-light-replacer-no-lights = It's empty. +comp-light-replacer-has-lights = It contains the following: +comp-light-replacer-light-listing = {$amount -> + [one] [color=yellow]{$amount}[/color] [color=gray]{$name}[/color] + *[other] [color=yellow]{$amount}[/color] [color=gray]{$name}s[/color] +} \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml index 208843ad19..e0282ce830 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml @@ -11,12 +11,10 @@ sprite: Objects/Specific/Janitorial/light_replacer.rsi - type: LightReplacer contents: - - name: LightTube + - id: LightTube amount: 8 - type: Tube - - name: LightBulb + - id: LightBulb amount: 5 - type: Bulb - type: Tag tags: - DroneUsable @@ -25,3 +23,13 @@ - type: ContainerContainer containers: light_replacer_storage: !type:Container + +- type: entity + parent: LightReplacer + id: LightReplacerEmpty + suffix: Empty + components: + - type: LightReplacer + contents: + - id: LightTube + amount: 0 diff --git a/Resources/Prototypes/Recipes/Lathes/janitorial.yml b/Resources/Prototypes/Recipes/Lathes/janitorial.yml index a9a2866c1d..6165bf274c 100644 --- a/Resources/Prototypes/Recipes/Lathes/janitorial.yml +++ b/Resources/Prototypes/Recipes/Lathes/janitorial.yml @@ -50,11 +50,11 @@ - type: latheRecipe id: LightReplacer - result: LightReplacer + result: LightReplacerEmpty completetime: 2.4 materials: Steel: 100 - Glass: 1000 + Glass: 500 - type: latheRecipe id: AdvMopItem