using Content.Shared.Light.Components;
+using Content.Shared.Light.EntitySystems;
using Robust.Client.GameObjects;
-namespace Content.Client.Light.Visualizers;
+namespace Content.Client.Light.EntitySystems;
-public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
+public sealed class LightBulbSystem : SharedLightBulbSystem
{
- protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
+ [Dependency] private readonly AppearanceSystem _appearance = default!;
+ [Dependency] private readonly SpriteSystem _sprite = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<LightBulbComponent, AppearanceChangeEvent>(OnAppearanceChange);
+ }
+
+ private void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// update sprite state
- if (AppearanceSystem.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
+ if (_appearance.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
{
switch (state)
{
case LightBulbState.Normal:
- SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
+ _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break;
case LightBulbState.Broken:
- SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
+ _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break;
case LightBulbState.Burned:
- SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
+ _sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break;
}
}
// also update sprites color
- if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
+ if (_appearance.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{
- SpriteSystem.SetColor((uid, args.Sprite), color);
+ _sprite.SetColor((uid, args.Sprite), color);
}
}
}
--- /dev/null
+using Content.Shared.Light.EntitySystems;
+
+namespace Content.Client.Light.EntitySystems;
+
+public sealed class PoweredLightSystem : SharedPoweredLightSystem;
[RegisterComponent]
public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent
{
+ public override float Load { get; set; }
}
using Content.Server.Administration.Logs;
-using Content.Server.Light.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Jittering;
+using Content.Shared.Light.Components;
using Content.Shared.Maps;
using Content.Shared.NodeContainer;
using Content.Shared.NodeContainer.NodeGroups;
-using Content.Server.Light.Components;
-using Content.Shared.Destructible;
-using Content.Shared.Light.Components;
-using Content.Shared.Throwing;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
+using Content.Shared.Light.EntitySystems;
-namespace Content.Server.Light.EntitySystems
-{
- public sealed class LightBulbSystem : EntitySystem
- {
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
+namespace Content.Server.Light.EntitySystems;
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<LightBulbComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<LightBulbComponent, LandEvent>(HandleLand);
- SubscribeLocalEvent<LightBulbComponent, BreakageEventArgs>(OnBreak);
- }
-
- private void OnInit(EntityUid uid, LightBulbComponent bulb, ComponentInit args)
- {
- // update default state of bulbs
- SetColor(uid, bulb.Color, bulb);
- SetState(uid, bulb.State, bulb);
- }
-
- private void HandleLand(EntityUid uid, LightBulbComponent bulb, ref LandEvent args)
- {
- PlayBreakSound(uid, bulb);
- SetState(uid, LightBulbState.Broken, bulb);
- }
-
- private void OnBreak(EntityUid uid, LightBulbComponent component, BreakageEventArgs args)
- {
- SetState(uid, LightBulbState.Broken, component);
- }
-
- /// <summary>
- /// Set a new color for a light bulb and raise event about change
- /// </summary>
- public void SetColor(EntityUid uid, Color color, LightBulbComponent? bulb = null)
- {
- if (!Resolve(uid, ref bulb))
- return;
-
- bulb.Color = color;
- UpdateAppearance(uid, bulb);
- }
-
- /// <summary>
- /// Set a new state for a light bulb (broken, burned) and raise event about change
- /// </summary>
- public void SetState(EntityUid uid, LightBulbState state, LightBulbComponent? bulb = null)
- {
- if (!Resolve(uid, ref bulb))
- return;
-
- bulb.State = state;
- UpdateAppearance(uid, bulb);
- }
-
- public void PlayBreakSound(EntityUid uid, LightBulbComponent? bulb = null)
- {
- if (!Resolve(uid, ref bulb))
- return;
-
- _audio.PlayPvs(bulb.BreakSound, uid);
- }
-
- private void UpdateAppearance(EntityUid uid, LightBulbComponent? bulb = null,
- AppearanceComponent? appearance = null)
- {
- if (!Resolve(uid, ref bulb, ref appearance, logMissing: false))
- return;
-
- // try to update appearance and color
- _appearance.SetData(uid, LightBulbVisuals.State, bulb.State, appearance);
- _appearance.SetData(uid, LightBulbVisuals.Color, bulb.Color, appearance);
- }
- }
-}
+public sealed class LightBulbSystem : SharedLightBulbSystem;
-using Content.Server.DeviceLinking.Systems;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Systems;
using Content.Server.Emp;
using Content.Server.Ghost;
-using Content.Server.Light.Components;
-using Content.Server.Power.Components;
-using Content.Shared.Audio;
-using Content.Shared.Damage;
-using Content.Shared.DeviceLinking.Events;
-using Content.Shared.DoAfter;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Interaction;
-using Content.Shared.Light;
using Content.Shared.Light.Components;
-using Robust.Server.GameObjects;
-using Robust.Shared.Containers;
-using Robust.Shared.Timing;
-using Robust.Shared.Audio.Systems;
-using Content.Shared.Damage.Systems;
-using Content.Shared.Damage.Components;
-using Content.Shared.DeviceNetwork;
-using Content.Shared.DeviceNetwork.Events;
-using Content.Shared.Power;
+using Content.Shared.Light.EntitySystems;
-namespace Content.Server.Light.EntitySystems
+namespace Content.Server.Light.EntitySystems;
+
+/// <summary>
+/// System for the PoweredLightComponents
+/// </summary>
+public sealed class PoweredLightSystem : SharedPoweredLightSystem
{
- /// <summary>
- /// System for the PoweredLightComponents
- /// </summary>
- public sealed class PoweredLightSystem : EntitySystem
+ public override void Initialize()
{
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly SharedAmbientSoundSystem _ambientSystem = default!;
- [Dependency] private readonly LightBulbSystem _bulbSystem = default!;
- [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
- [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
- [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly PointLightSystem _pointLight = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly DamageOnInteractSystem _damageOnInteractSystem = default!;
-
- private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2);
- public const string LightBulbContainer = "light_bulb";
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<PoweredLightComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<PoweredLightComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<PoweredLightComponent, InteractUsingEvent>(OnInteractUsing);
- SubscribeLocalEvent<PoweredLightComponent, InteractHandEvent>(OnInteractHand);
-
- SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
- SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
-
- SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
- SubscribeLocalEvent<PoweredLightComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
-
- SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
-
- SubscribeLocalEvent<PoweredLightComponent, PoweredLightDoAfterEvent>(OnDoAfter);
- SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
- }
-
- private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
- {
- light.LightBulbContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, LightBulbContainer);
- _signalSystem.EnsureSinkPorts(uid, light.OnPort, light.OffPort, light.TogglePort);
- }
+ base.Initialize();
+ SubscribeLocalEvent<PoweredLightComponent, MapInitEvent>(OnMapInit);
- private void OnMapInit(EntityUid uid, PoweredLightComponent light, MapInitEvent args)
- {
- // TODO: Use ContainerFill dog
- if (light.HasLampOnSpawn != null)
- {
- var entity = Spawn(light.HasLampOnSpawn, Comp<TransformComponent>(uid).Coordinates);
- _containerSystem.Insert(entity, light.LightBulbContainer);
- }
- // need this to update visualizers
- UpdateLight(uid, light);
- }
-
- private void OnInteractUsing(EntityUid uid, PoweredLightComponent component, InteractUsingEvent args)
- {
- if (args.Handled)
- return;
-
- args.Handled = InsertBulb(uid, args.Used, component);
- }
-
- private void OnInteractHand(EntityUid uid, PoweredLightComponent light, InteractHandEvent args)
- {
- if (args.Handled)
- return;
-
- // check if light has bulb to eject
- var bulbUid = GetBulb(uid, light);
- if (bulbUid == null)
- return;
-
- var userUid = args.User;
- //removing a broken/burned bulb, so allow instant removal
- if(TryComp<LightBulbComponent>(bulbUid.Value, out var bulb) && bulb.State != LightBulbState.Normal)
- {
- args.Handled = EjectBulb(uid, userUid, light) != null;
- return;
- }
-
- // removing a working bulb, so require a delay
- _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, userUid, light.EjectBulbDelay, new PoweredLightDoAfterEvent(), uid, target: uid)
- {
- BreakOnMove = true,
- BreakOnDamage = true,
- });
-
- args.Handled = true;
- }
-
- #region Bulb Logic API
- /// <summary>
- /// Inserts the bulb if possible.
- /// </summary>
- /// <returns>True if it could insert it, false if it couldn't.</returns>
- public bool InsertBulb(EntityUid uid, EntityUid bulbUid, PoweredLightComponent? light = null)
- {
- if (!Resolve(uid, ref light))
- return false;
-
- // check if light already has bulb
- if (GetBulb(uid, light) != null)
- return false;
-
- // check if bulb fits
- if (!TryComp(bulbUid, out LightBulbComponent? lightBulb))
- return false;
- if (lightBulb.Type != light.BulbType)
- return false;
-
- // try to insert bulb in container
- if (!_containerSystem.Insert(bulbUid, light.LightBulbContainer))
- return false;
-
- UpdateLight(uid, light);
- return true;
- }
-
- /// <summary>
- /// Ejects the bulb to a mob's hand if possible.
- /// </summary>
- /// <returns>Bulb uid if it was successfully ejected, null otherwise</returns>
- public EntityUid? EjectBulb(EntityUid uid, EntityUid? userUid = null, PoweredLightComponent? light = null)
- {
- if (!Resolve(uid, ref light))
- return null;
-
- // check if light has bulb
- if (GetBulb(uid, light) is not { Valid: true } bulb)
- return null;
-
- // try to remove bulb from container
- if (!_containerSystem.Remove(bulb, light.LightBulbContainer))
- return null;
-
- // try to place bulb in hands
- _handsSystem.PickupOrDrop(userUid, bulb);
-
- UpdateLight(uid, light);
- return bulb;
- }
-
- /// <summary>
- /// Replaces the spawned prototype of a pre-mapinit powered light with a different variant.
- /// </summary>
- public bool ReplaceSpawnedPrototype(Entity<PoweredLightComponent> light, string bulb)
- {
- if (light.Comp.LightBulbContainer.ContainedEntity != null)
- return false;
-
- if (LifeStage(light.Owner) >= EntityLifeStage.MapInitialized)
- return false;
-
- light.Comp.HasLampOnSpawn = bulb;
- return true;
- }
-
- /// <summary>
- /// Try to replace current bulb with a new one
- /// If succeed old bulb just drops on floor
- /// </summary>
- public bool ReplaceBulb(EntityUid uid, EntityUid bulb, PoweredLightComponent? light = null)
- {
- EjectBulb(uid, null, light);
- return InsertBulb(uid, bulb, light);
- }
-
- /// <summary>
- /// Try to get light bulb inserted in powered light
- /// </summary>
- /// <returns>Bulb uid if it exist, null otherwise</returns>
- public EntityUid? GetBulb(EntityUid uid, PoweredLightComponent? light = null)
- {
- if (!Resolve(uid, ref light))
- return null;
-
- return light.LightBulbContainer.ContainedEntity;
- }
-
- /// <summary>
- /// Try to break bulb inside light fixture
- /// </summary>
- public bool TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null)
- {
- if (!Resolve(uid, ref light, false))
- return false;
-
- // if we aren't mapinited,
- // just null the spawned bulb
- if (LifeStage(uid) < EntityLifeStage.MapInitialized)
- {
- light.HasLampOnSpawn = null;
- return true;
- }
-
- // check bulb state
- var bulbUid = GetBulb(uid, light);
- if (bulbUid == null || !TryComp(bulbUid.Value, out LightBulbComponent? lightBulb))
- return false;
- if (lightBulb.State == LightBulbState.Broken)
- return false;
-
- // break it
- _bulbSystem.SetState(bulbUid.Value, LightBulbState.Broken, lightBulb);
- _bulbSystem.PlayBreakSound(bulbUid.Value, lightBulb);
- UpdateLight(uid, light);
- return true;
- }
- #endregion
+ SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
- private void UpdateLight(EntityUid uid,
- PoweredLightComponent? light = null,
- ApcPowerReceiverComponent? powerReceiver = null,
- AppearanceComponent? appearance = null)
- {
- if (!Resolve(uid, ref light, ref powerReceiver, false))
- return;
-
- // Optional component.
- Resolve(uid, ref appearance, false);
-
- // check if light has bulb
- var bulbUid = GetBulb(uid, light);
- if (bulbUid == null || !TryComp(bulbUid.Value, out LightBulbComponent? lightBulb))
- {
- SetLight(uid, false, light: light);
- powerReceiver.Load = 0;
- _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Empty, appearance);
- return;
- }
-
- switch (lightBulb.State)
- {
- case LightBulbState.Normal:
- if (powerReceiver.Powered && light.On)
- {
- SetLight(uid, true, lightBulb.Color, light, lightBulb.LightRadius, lightBulb.LightEnergy, lightBulb.LightSoftness);
- _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.On, appearance);
- var time = _gameTiming.CurTime;
- if (time > light.LastThunk + ThunkDelay)
- {
- light.LastThunk = time;
- _audio.PlayPvs(light.TurnOnSound, uid, light.TurnOnSound.Params.AddVolume(-10f));
- }
- }
- else
- {
- SetLight(uid, false, light: light);
- _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Off, appearance);
- }
- break;
- case LightBulbState.Broken:
- SetLight(uid, false, light: light);
- _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Broken, appearance);
- break;
- case LightBulbState.Burned:
- SetLight(uid, false, light: light);
- _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Burned, appearance);
- break;
- }
-
- powerReceiver.Load = (light.On && lightBulb.State == LightBulbState.Normal) ? lightBulb.PowerUse : 0;
- }
-
- /// <summary>
- /// Destroy the light bulb if the light took any damage.
- /// </summary>
- public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args)
- {
- // Was it being repaired, or did it take damage?
- if (args.DamageIncreased)
- {
- // Eventually, this logic should all be done by this (or some other) system, not a component.
- TryDestroyBulb(uid, component);
- }
- }
-
- private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)
- {
- if (light.IgnoreGhostsBoo)
- return;
-
- // check cooldown first to prevent abuse
- var time = _gameTiming.CurTime;
- if (light.LastGhostBlink != null)
- {
- if (time <= light.LastGhostBlink + light.GhostBlinkingCooldown)
- return;
- }
-
- light.LastGhostBlink = time;
-
- ToggleBlinkingLight(uid, light, true);
- uid.SpawnTimer(light.GhostBlinkingTime, () =>
- {
- ToggleBlinkingLight(uid, light, false);
- });
-
- args.Handled = true;
- }
-
- private void OnPowerChanged(EntityUid uid, PoweredLightComponent component, ref PowerChangedEvent args)
- {
- // TODO: Power moment
- var metadata = MetaData(uid);
-
- if (metadata.EntityPaused || TerminatingOrDeleted(uid, metadata))
- return;
-
- UpdateLight(uid, component);
- }
-
- public void ToggleBlinkingLight(EntityUid uid, PoweredLightComponent light, bool isNowBlinking)
- {
- if (light.IsBlinking == isNowBlinking)
- return;
-
- light.IsBlinking = isNowBlinking;
-
- if (!TryComp(uid, out AppearanceComponent? appearance))
- return;
-
- _appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance);
- }
-
- private void OnSignalReceived(EntityUid uid, PoweredLightComponent component, ref SignalReceivedEvent args)
- {
- if (args.Port == component.OffPort)
- SetState(uid, false, component);
- else if (args.Port == component.OnPort)
- SetState(uid, true, component);
- else if (args.Port == component.TogglePort)
- ToggleLight(uid, component);
- }
-
- /// <summary>
- /// Turns the light on or of when receiving a <see cref="DeviceNetworkConstants.CmdSetState"/> command.
- /// The light is turned on or of according to the <see cref="DeviceNetworkConstants.StateEnabled"/> value
- /// </summary>
- private void OnPacketReceived(EntityUid uid, PoweredLightComponent component, DeviceNetworkPacketEvent args)
- {
- if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return;
- if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return;
+ SubscribeLocalEvent<PoweredLightComponent, EmpPulseEvent>(OnEmpPulse);
+ }
- SetState(uid, enabled, component);
- }
+ private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)
+ {
+ if (light.IgnoreGhostsBoo)
+ return;
- private void SetLight(EntityUid uid, bool value, Color? color = null, PoweredLightComponent? light = null, float? radius = null, float? energy = null, float? softness = null)
+ // check cooldown first to prevent abuse
+ var time = GameTiming.CurTime;
+ if (light.LastGhostBlink != null)
{
- if (!Resolve(uid, ref light))
+ if (time <= light.LastGhostBlink + light.GhostBlinkingCooldown)
return;
-
- light.CurrentLit = value;
- _ambientSystem.SetAmbience(uid, value);
-
- if (TryComp(uid, out PointLightComponent? pointLight))
- {
- _pointLight.SetEnabled(uid, value, pointLight);
-
- if (color != null)
- _pointLight.SetColor(uid, color.Value, pointLight);
- if (radius != null)
- _pointLight.SetRadius(uid, (float) radius, pointLight);
- if (energy != null)
- _pointLight.SetEnergy(uid, (float) energy, pointLight);
- if (softness != null)
- _pointLight.SetSoftness(uid, (float) softness, pointLight);
- }
-
- // light bulbs burn your hands!
- if (TryComp<DamageOnInteractComponent>(uid, out var damageOnInteractComp))
- _damageOnInteractSystem.SetIsDamageActiveTo((uid, damageOnInteractComp), value);
}
- public void ToggleLight(EntityUid uid, PoweredLightComponent? light = null)
- {
- if (!Resolve(uid, ref light))
- return;
-
- light.On = !light.On;
- UpdateLight(uid, light);
- }
+ light.LastGhostBlink = time;
- public void SetState(EntityUid uid, bool state, PoweredLightComponent? light = null)
+ ToggleBlinkingLight(uid, light, true);
+ uid.SpawnTimer(light.GhostBlinkingTime, () =>
{
- if (!Resolve(uid, ref light))
- return;
+ ToggleBlinkingLight(uid, light, false);
+ });
- light.On = state;
- UpdateLight(uid, light);
- }
+ args.Handled = true;
+ }
- private void OnDoAfter(EntityUid uid, PoweredLightComponent component, DoAfterEvent args)
+ private void OnMapInit(EntityUid uid, PoweredLightComponent light, MapInitEvent args)
+ {
+ // TODO: Use ContainerFill dog
+ if (light.HasLampOnSpawn != null)
{
- if (args.Handled || args.Cancelled || args.Args.Target == null)
- return;
-
- EjectBulb(args.Args.Target.Value, args.Args.User, component);
-
- args.Handled = true;
+ var entity = EntityManager.SpawnEntity(light.HasLampOnSpawn, EntityManager.GetComponent<TransformComponent>(uid).Coordinates);
+ ContainerSystem.Insert(entity, light.LightBulbContainer);
}
+ // need this to update visualizers
+ UpdateLight(uid, light);
+ }
- private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
- {
- if (TryDestroyBulb(uid, component))
- args.Affected = true;
- }
+ private void OnEmpPulse(EntityUid uid, PoweredLightComponent component, ref EmpPulseEvent args)
+ {
+ if (TryDestroyBulb(uid, component))
+ args.Affected = true;
}
}
/// <summary>
/// Amount of charge this needs from an APC per second to function.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
[DataField("powerLoad")]
- public float Load { get => NetworkLoad.DesiredPower; set => NetworkLoad.DesiredPower = value; }
+ public override float Load
+ {
+ get => NetworkLoad.DesiredPower;
+ set => NetworkLoad.DesiredPower = value;
+ }
public ApcPowerProviderComponent? Provider = null;
using Content.Shared.Emag.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
+using Content.Shared.Light.Components;
using Content.Shared.Maps;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.GameTicking.Components;
+using Content.Shared.Light.Components;
namespace Content.Server.StationEvents.Events;
using Content.Server.Ghost;
-using Content.Server.Light.Components;
using Content.Server.Xenoarchaeology.Artifact.XAE.Components;
+using Content.Shared.Light.Components;
using Content.Shared.Xenoarchaeology.Artifact;
using Content.Shared.Xenoarchaeology.Artifact.XAE;
using Robust.Shared.Random;
/// Component that represents a light bulb. Can be broken, or burned, which turns them mostly useless.
/// TODO: Breaking and burning should probably be moved to another component eventually.
/// </summary>
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class LightBulbComponent : Component
{
/// <summary>
/// The color of the lightbulb and the light it produces.
/// </summary>
- [DataField("color")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField, AutoNetworkedField]
public Color Color = Color.White;
/// <summary>
/// The type of lightbulb. Tube/bulb/etc...
/// </summary>
[DataField("bulb")]
- [ViewVariables(VVAccess.ReadWrite)]
public LightBulbType Type = LightBulbType.Tube;
/// <summary>
/// The initial state of the lightbulb.
/// </summary>
- [DataField("startingState")]
+ [DataField("startingState"), AutoNetworkedField]
public LightBulbState State = LightBulbState.Normal;
/// <summary>
/// The temperature the air around the lightbulb is exposed to when the lightbulb burns out.
/// </summary>
[DataField("BurningTemperature")]
- [ViewVariables(VVAccess.ReadWrite)]
public int BurningTemperature = 1400;
/// <summary>
/// Relates to how bright the light produced by the lightbulb is.
/// </summary>
- [DataField("lightEnergy")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public float LightEnergy = 0.8f;
/// <summary>
/// The maximum radius of the point light source this light produces.
/// </summary>
- [DataField("lightRadius")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public float LightRadius = 10;
/// <summary>
/// Relates to the falloff constant of the light produced by the lightbulb.
/// </summary>
- [DataField("lightSoftness")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public float LightSoftness = 1;
/// <summary>
/// The amount of power used by the lightbulb when it's active.
/// </summary>
[DataField("PowerUse")]
- [ViewVariables(VVAccess.ReadWrite)]
public int PowerUse = 60;
/// <summary>
/// The sound produced when the lightbulb breaks.
/// </summary>
- [DataField("breakSound")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public SoundSpecifier BreakSound = new SoundCollectionSpecifier("GlassBreak", AudioParams.Default.WithVolume(-6f));
#region Appearance
/// <summary>
/// The sprite state used when the lightbulb is intact.
/// </summary>
- [DataField("normalSpriteState")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public string NormalSpriteState = "normal";
/// <summary>
/// The sprite state used when the lightbulb is broken.
/// </summary>
- [DataField("brokenSpriteState")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public string BrokenSpriteState = "broken";
/// <summary>
/// The sprite state used when the lightbulb is burned.
/// </summary>
- [DataField("burnedSpriteState")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public string BurnedSpriteState = "burned";
#endregion Appearance
+using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.Storage;
using Robust.Shared.Audio;
-using Content.Server.Light.EntitySystems;
-using Content.Shared.Damage;
using Content.Shared.DeviceLinking;
-using Content.Shared.Light.Components;
+using Content.Shared.Light.EntitySystems;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Light.Components
+namespace Content.Shared.Light.Components
{
/// <summary>
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
/// </summary>
- [RegisterComponent, Access(typeof(PoweredLightSystem))]
+ [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause, Access(typeof(SharedPoweredLightSystem))]
public sealed partial class PoweredLightComponent : Component
{
- [DataField("burnHandSound")]
+ /*
+ * Stop adding more fields, use components or I will shed you.
+ */
+
+ [DataField]
public SoundSpecifier BurnHandSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
- [DataField("turnOnSound")]
+ [DataField]
public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Machines/light_tube_on.ogg");
- [DataField("hasLampOnSpawn", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string? HasLampOnSpawn = null;
+ // Should be using containerfill?
+ [DataField]
+ public EntProtoId? HasLampOnSpawn = null;
[DataField("bulb")]
public LightBulbType BulbType;
- [DataField("on")]
+ [DataField, AutoNetworkedField]
public bool On = true;
- [DataField("ignoreGhostsBoo")]
+ [DataField]
public bool IgnoreGhostsBoo;
- [DataField("ghostBlinkingTime")]
+ [DataField]
public TimeSpan GhostBlinkingTime = TimeSpan.FromSeconds(10);
- [DataField("ghostBlinkingCooldown")]
+ [DataField]
public TimeSpan GhostBlinkingCooldown = TimeSpan.FromSeconds(60);
[ViewVariables]
public ContainerSlot LightBulbContainer = default!;
- [ViewVariables]
+
+ [AutoNetworkedField]
public bool CurrentLit;
- [ViewVariables]
+
+ [DataField, AutoNetworkedField]
public bool IsBlinking;
- [ViewVariables]
+
+ [DataField, AutoNetworkedField, AutoPausedField]
public TimeSpan LastThunk;
- [ViewVariables]
+
+ [DataField, AutoPausedField]
public TimeSpan? LastGhostBlink;
- [DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
- public string OnPort = "On";
+ [DataField]
+ public ProtoId<SinkPortPrototype> OnPort = "On";
- [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
- public string OffPort = "Off";
+ [DataField]
+ public ProtoId<SinkPortPrototype> OffPort = "Off";
- [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
- public string TogglePort = "Toggle";
+ [DataField]
+ public ProtoId<SinkPortPrototype> TogglePort = "Toggle";
/// <summary>
/// How long it takes to eject a bulb from this
/// </summary>
- [DataField("ejectBulbDelay")]
+ [DataField]
public float EjectBulbDelay = 2;
/// <summary>
/// Shock damage done to a mob that hits the light with an unarmed attack
/// </summary>
- [DataField("unarmedHitShock")]
+ [DataField]
public int UnarmedHitShock = 20;
/// <summary>
/// Stun duration applied to a mob that hits the light with an unarmed attack
/// </summary>
- [DataField("unarmedHitStun")]
+ [DataField]
public TimeSpan UnarmedHitStun = TimeSpan.FromSeconds(5);
}
}
--- /dev/null
+using Content.Shared.Destructible;
+using Content.Shared.Light.Components;
+using Content.Shared.Throwing;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Shared.Light.EntitySystems;
+
+public abstract class SharedLightBulbSystem : EntitySystem
+{
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<LightBulbComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<LightBulbComponent, LandEvent>(HandleLand);
+ SubscribeLocalEvent<LightBulbComponent, BreakageEventArgs>(OnBreak);
+ }
+
+ private void OnInit(EntityUid uid, LightBulbComponent bulb, ComponentInit args)
+ {
+ // update default state of bulbs
+ SetColor(uid, bulb.Color, bulb);
+ SetState(uid, bulb.State, bulb);
+ }
+
+ private void HandleLand(EntityUid uid, LightBulbComponent bulb, ref LandEvent args)
+ {
+ PlayBreakSound(uid, bulb);
+ SetState(uid, LightBulbState.Broken, bulb);
+ }
+
+ private void OnBreak(EntityUid uid, LightBulbComponent component, BreakageEventArgs args)
+ {
+ SetState(uid, LightBulbState.Broken, component);
+ }
+
+ /// <summary>
+ /// Set a new color for a light bulb and raise event about change
+ /// </summary>
+ public void SetColor(EntityUid uid, Color color, LightBulbComponent? bulb = null)
+ {
+ if (!Resolve(uid, ref bulb) || bulb.Color.Equals(color))
+ return;
+
+ bulb.Color = color;
+ Dirty(uid, bulb);
+ UpdateAppearance(uid, bulb);
+ }
+
+ /// <summary>
+ /// Set a new state for a light bulb (broken, burned) and raise event about change
+ /// </summary>
+ public void SetState(EntityUid uid, LightBulbState state, LightBulbComponent? bulb = null)
+ {
+ if (!Resolve(uid, ref bulb) || bulb.State == state)
+ return;
+
+ bulb.State = state;
+ Dirty(uid, bulb);
+ UpdateAppearance(uid, bulb);
+ }
+
+ public void PlayBreakSound(EntityUid uid, LightBulbComponent? bulb = null, EntityUid? user = null)
+ {
+ if (!Resolve(uid, ref bulb))
+ return;
+
+ _audio.PlayPredicted(bulb.BreakSound, uid, user: user);
+ }
+
+ private void UpdateAppearance(EntityUid uid, LightBulbComponent? bulb = null,
+ AppearanceComponent? appearance = null)
+ {
+ if (!Resolve(uid, ref bulb, ref appearance, logMissing: false))
+ return;
+
+ // try to update appearance and color
+ _appearance.SetData(uid, LightBulbVisuals.State, bulb.State, appearance);
+ _appearance.SetData(uid, LightBulbVisuals.Color, bulb.Color, appearance);
+ }
+}
--- /dev/null
+using Content.Shared.Audio;
+using Content.Shared.Damage;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Systems;
+using Content.Shared.DeviceLinking;
+using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DoAfter;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Interaction;
+using Content.Shared.Light.Components;
+using Content.Shared.Power;
+using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
+using Content.Shared.Storage.EntitySystems;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Containers;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Light.EntitySystems;
+
+public abstract class SharedPoweredLightSystem : EntitySystem
+{
+ [Dependency] protected readonly IGameTiming GameTiming = default!;
+ [Dependency] private readonly DamageOnInteractSystem _damageOnInteractSystem = default!;
+ [Dependency] private readonly SharedAmbientSoundSystem _ambientSystem = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+ [Dependency] private readonly SharedLightBulbSystem _bulbSystem = default!;
+ [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly SharedPowerReceiverSystem _receiver = default!;
+ [Dependency] private readonly SharedPointLightSystem _pointLight = default!;
+ [Dependency] private readonly SharedStorageSystem _storage = default!;
+ [Dependency] private readonly SharedDeviceLinkSystem _deviceLink = default!;
+
+ private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2);
+ public const string LightBulbContainer = "light_bulb";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<PoweredLightComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<PoweredLightComponent, EntRemovedFromContainerMessage>(OnRemoved);
+ SubscribeLocalEvent<PoweredLightComponent, EntInsertedIntoContainerMessage>(OnInserted);
+ SubscribeLocalEvent<PoweredLightComponent, InteractUsingEvent>(OnInteractUsing);
+ SubscribeLocalEvent<PoweredLightComponent, InteractHandEvent>(OnInteractHand);
+ SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
+ SubscribeLocalEvent<PoweredLightComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+ SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
+ SubscribeLocalEvent<PoweredLightComponent, PoweredLightDoAfterEvent>(OnDoAfter);
+ SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
+ }
+
+ private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
+ {
+ light.LightBulbContainer = ContainerSystem.EnsureContainer<ContainerSlot>(uid, LightBulbContainer);
+ _deviceLink.EnsureSinkPorts(uid, light.OnPort, light.OffPort, light.TogglePort);
+ }
+
+ private void OnRemoved(Entity<PoweredLightComponent> light, ref EntRemovedFromContainerMessage args)
+ {
+ if (args.Container.ID != LightBulbContainer)
+ return;
+
+ UpdateLight(light, light);
+ }
+
+ private void OnInserted(Entity<PoweredLightComponent> light, ref EntInsertedIntoContainerMessage args)
+ {
+ if (args.Container.ID != LightBulbContainer)
+ return;
+
+ UpdateLight(light, light);
+ }
+
+ private void OnInteractUsing(EntityUid uid, PoweredLightComponent component, InteractUsingEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ args.Handled = InsertBulb(uid, args.Used, component, user: args.User, playAnimation: true);
+ }
+
+ private void OnInteractHand(EntityUid uid, PoweredLightComponent light, InteractHandEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ // check if light has bulb to eject
+ var bulbUid = GetBulb(uid, light);
+ if (bulbUid == null)
+ return;
+
+ var userUid = args.User;
+ //removing a broken/burned bulb, so allow instant removal
+ if (TryComp<LightBulbComponent>(bulbUid.Value, out var bulb) && bulb.State != LightBulbState.Normal)
+ {
+ args.Handled = EjectBulb(uid, userUid, light) != null;
+ return;
+ }
+
+ // removing a working bulb, so require a delay
+ _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, userUid, light.EjectBulbDelay, new PoweredLightDoAfterEvent(), uid, target: uid)
+ {
+ BreakOnMove = true,
+ BreakOnDamage = true,
+ });
+
+ args.Handled = true;
+ }
+
+ private void OnSignalReceived(Entity<PoweredLightComponent> ent, ref SignalReceivedEvent args)
+ {
+ if (args.Port == ent.Comp.OffPort)
+ SetState(ent, false, ent.Comp);
+ else if (args.Port == ent.Comp.OnPort)
+ SetState(ent, true, ent.Comp);
+ else if (args.Port == ent.Comp.TogglePort)
+ ToggleLight(ent, ent.Comp);
+ }
+
+ /// <summary>
+ /// Turns the light on or of when receiving a <see cref="DeviceNetworkConstants.CmdSetState"/> command.
+ /// The light is turned on or of according to the <see cref="DeviceNetworkConstants.StateEnabled"/> value
+ /// </summary>
+ private void OnPacketReceived(EntityUid uid, PoweredLightComponent component, DeviceNetworkPacketEvent args)
+ {
+ if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return;
+ if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return;
+
+ SetState(uid, enabled, component);
+ }
+
+ /// <summary>
+ /// Inserts the bulb if possible.
+ /// </summary>
+ /// <returns>True if it could insert it, false if it couldn't.</returns>
+ public bool InsertBulb(EntityUid uid, EntityUid bulbUid, PoweredLightComponent? light = null, EntityUid? user = null, bool playAnimation = false)
+ {
+ if (!Resolve(uid, ref light))
+ return false;
+
+ // check if light already has bulb
+ if (GetBulb(uid, light) != null)
+ return false;
+
+ // check if bulb fits
+ if (!TryComp<LightBulbComponent>(bulbUid, out var lightBulb))
+ return false;
+
+ if (lightBulb.Type != light.BulbType)
+ return false;
+
+ // try to insert bulb in container
+ if (!ContainerSystem.Insert(bulbUid, light.LightBulbContainer))
+ return false;
+
+ if (playAnimation && TryComp(user, out TransformComponent? xform))
+ {
+ var itemXform = Transform(uid);
+ _storage.PlayPickupAnimation(bulbUid, xform.Coordinates, itemXform.Coordinates, itemXform.LocalRotation, user: user);
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Ejects the bulb to a mob's hand if possible.
+ /// </summary>
+ /// <returns>Bulb uid if it was successfully ejected, null otherwise</returns>
+ public EntityUid? EjectBulb(EntityUid uid, EntityUid? userUid = null, PoweredLightComponent? light = null)
+ {
+ if (!Resolve(uid, ref light))
+ return null;
+
+ // check if light has bulb
+ if (GetBulb(uid, light) is not { Valid: true } bulb)
+ return null;
+
+ // try to remove bulb from container
+ if (!ContainerSystem.Remove(bulb, light.LightBulbContainer))
+ return null;
+
+ // try to place bulb in hands
+ _handsSystem.PickupOrDrop(userUid, bulb);
+
+ return bulb;
+ }
+
+ /// <summary>
+ /// Replaces the spawned prototype of a pre-mapinit powered light with a different variant.
+ /// </summary>
+ public bool ReplaceSpawnedPrototype(Entity<PoweredLightComponent> light, string bulb)
+ {
+ if (light.Comp.LightBulbContainer.ContainedEntity != null)
+ return false;
+
+ if (LifeStage(light.Owner) >= EntityLifeStage.MapInitialized)
+ return false;
+
+ light.Comp.HasLampOnSpawn = bulb;
+ return true;
+ }
+
+ /// <summary>
+ /// Try to replace current bulb with a new one
+ /// If succeed old bulb just drops on floor
+ /// </summary>
+ public bool ReplaceBulb(EntityUid uid, EntityUid bulb, PoweredLightComponent? light = null)
+ {
+ EjectBulb(uid, null, light);
+ return InsertBulb(uid, bulb, light);
+ }
+
+ /// <summary>
+ /// Try to get light bulb inserted in powered light
+ /// </summary>
+ /// <returns>Bulb uid if it exist, null otherwise</returns>
+ public EntityUid? GetBulb(EntityUid uid, PoweredLightComponent? light = null)
+ {
+ if (!Resolve(uid, ref light))
+ return null;
+
+ return light.LightBulbContainer?.ContainedEntity;
+ }
+
+ /// <summary>
+ /// Try to break bulb inside light fixture
+ /// </summary>
+ public bool TryDestroyBulb(EntityUid uid, PoweredLightComponent? light = null)
+ {
+ if (!Resolve(uid, ref light, false))
+ return false;
+
+ // if we aren't mapinited,
+ // just null the spawned bulb
+ if (LifeStage(uid) < EntityLifeStage.MapInitialized)
+ {
+ light.HasLampOnSpawn = null;
+ return true;
+ }
+
+ // check bulb state
+ var bulbUid = GetBulb(uid, light);
+ if (bulbUid == null || !EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb))
+ return false;
+ if (lightBulb.State == LightBulbState.Broken)
+ return false;
+
+ // break it
+ _bulbSystem.SetState(bulbUid.Value, LightBulbState.Broken, lightBulb);
+ _bulbSystem.PlayBreakSound(bulbUid.Value, lightBulb);
+ UpdateLight(uid, light);
+ return true;
+ }
+
+ protected void UpdateLight(EntityUid uid,
+ PoweredLightComponent? light = null,
+ SharedApcPowerReceiverComponent? powerReceiver = null,
+ AppearanceComponent? appearance = null,
+ EntityUid? user = null)
+ {
+ if (!Resolve(uid, ref light, false))
+ return;
+
+ if (!_receiver.ResolveApc(uid, ref powerReceiver))
+ return;
+
+ // Optional component.
+ Resolve(uid, ref appearance, false);
+
+ // check if light has bulb
+ var bulbUid = GetBulb(uid, light);
+ if (bulbUid == null || !TryComp<LightBulbComponent>(bulbUid.Value, out var lightBulb))
+ {
+ SetLight(uid, false, light: light);
+ powerReceiver.Load = 0;
+ _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Empty, appearance);
+ return;
+ }
+
+ switch (lightBulb.State)
+ {
+ case LightBulbState.Normal:
+ if (powerReceiver.Powered && light.On)
+ {
+ SetLight(uid, true, lightBulb.Color, light, lightBulb.LightRadius, lightBulb.LightEnergy, lightBulb.LightSoftness);
+ _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.On, appearance);
+ var time = GameTiming.CurTime;
+ if (time > light.LastThunk + ThunkDelay)
+ {
+ light.LastThunk = time;
+ Dirty(uid, light);
+ _audio.PlayPredicted(light.TurnOnSound, uid, user: user, light.TurnOnSound.Params.AddVolume(-10f));
+ }
+ }
+ else
+ {
+ SetLight(uid, false, light: light);
+ _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Off, appearance);
+ }
+ break;
+ case LightBulbState.Broken:
+ SetLight(uid, false, light: light);
+ _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Broken, appearance);
+ break;
+ case LightBulbState.Burned:
+ SetLight(uid, false, light: light);
+ _appearance.SetData(uid, PoweredLightVisuals.BulbState, PoweredLightState.Burned, appearance);
+ break;
+ }
+
+ powerReceiver.Load = (light.On && lightBulb.State == LightBulbState.Normal) ? lightBulb.PowerUse : 0;
+ }
+
+ /// <summary>
+ /// Destroy the light bulb if the light took any damage.
+ /// </summary>
+ public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args)
+ {
+ // Was it being repaired, or did it take damage?
+ if (args.DamageIncreased)
+ {
+ // Eventually, this logic should all be done by this (or some other) system, not a component.
+ TryDestroyBulb(uid, component);
+ }
+ }
+
+ private void OnPowerChanged(EntityUid uid, PoweredLightComponent component, ref PowerChangedEvent args)
+ {
+ // TODO: Power moment
+ var metadata = MetaData(uid);
+
+ if (metadata.EntityPaused || TerminatingOrDeleted(uid, metadata))
+ return;
+
+ UpdateLight(uid, component);
+ }
+
+ public void ToggleBlinkingLight(EntityUid uid, PoweredLightComponent light, bool isNowBlinking)
+ {
+ if (light.IsBlinking == isNowBlinking)
+ return;
+
+ light.IsBlinking = isNowBlinking;
+ Dirty(uid, light);
+
+ if (!TryComp<AppearanceComponent>(uid, out var appearance))
+ return;
+
+ _appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance);
+ }
+
+ private void SetLight(EntityUid uid, bool value, Color? color = null, PoweredLightComponent? light = null, float? radius = null, float? energy = null, float? softness = null)
+ {
+ if (!Resolve(uid, ref light))
+ return;
+
+ if (light.CurrentLit != value)
+ {
+ light.CurrentLit = value;
+ Dirty(uid, light);
+ }
+
+ _ambientSystem.SetAmbience(uid, value);
+
+ if (_pointLight.TryGetLight(uid, out var pointLight))
+ {
+ _pointLight.SetEnabled(uid, value, pointLight);
+
+ if (color != null)
+ _pointLight.SetColor(uid, color.Value, pointLight);
+ if (radius != null)
+ _pointLight.SetRadius(uid, (float)radius, pointLight);
+ if (energy != null)
+ _pointLight.SetEnergy(uid, (float)energy, pointLight);
+ if (softness != null)
+ _pointLight.SetSoftness(uid, (float)softness, pointLight);
+ }
+
+ // light bulbs burn your hands!
+ if (TryComp<DamageOnInteractComponent>(uid, out var damageOnInteractComp))
+ _damageOnInteractSystem.SetIsDamageActiveTo((uid, damageOnInteractComp), value);
+ }
+
+ public void ToggleLight(EntityUid uid, PoweredLightComponent? light = null)
+ {
+ if (!Resolve(uid, ref light))
+ return;
+
+ light.On = !light.On;
+ UpdateLight(uid, light);
+ }
+
+ public void SetState(EntityUid uid, bool state, PoweredLightComponent? light = null)
+ {
+ if (!Resolve(uid, ref light))
+ return;
+
+ light.On = state;
+ Dirty(uid, light);
+ UpdateLight(uid, light);
+ }
+
+ private void OnDoAfter(EntityUid uid, PoweredLightComponent component, DoAfterEvent args)
+ {
+ if (args.Handled || args.Cancelled || args.Args.Target == null)
+ return;
+
+ EjectBulb(args.Args.Target.Value, args.Args.User, component);
+
+ args.Handled = true;
+ }
+}
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool PowerDisabled { get; set; }
+
+ // Doesn't actually do anything on the client just here for shared code.
+ public abstract float Load { get; set; }
}