Code/Ultimate_Light_Manager.cs

A S&box component that implements an advanced light controller with presets, runtime controls, grouping and editor properties. It manages point/spot light components, supports effects like flicker, pulse, strobe, kelvin temperature conversion, sensors, audio, power groups, and synced runtime overrides for multiplayer.

NetworkingFile Access
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Dreams.UltimateLightManager;

[Library( "UltimateLightManager" )]
[Title( "Ultimate Light Manager" )]
[Description( "Advanced light component with presets, runtime controls, grouping, and an integrated S&box editor workflow." )]
[Category( "Light" )]
[Icon( "tungsten" )]
public class UltimateLightManager : Component, Component.ExecuteInEditor
{
    public enum LightTypeEnum
    {
        Point,
        Spot
    }

    public enum LightPreset
    {
        Custom,
        Candle,
        Torch,
        Neon,
        Alarm,
        BrokenLamp,
        SciFi,
        StreetLight
    }

    public static void SetGroupState( string groupName, bool isEnabled )
    {
        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.SetEnabledState( isEnabled );
        }
    }

    public static void SetGroupBrightness( string groupName, float brightness )
    {
        brightness = Math.Max( brightness, 0f );

        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.SetBrightnessLevel( brightness );
        }
    }

    public static void SetGroupColor( string groupName, Color color )
    {
        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.SetLightColorValue( color );
        }
    }

    public static void ApplyPresetToGroup( string groupName, LightPreset preset )
    {
        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.ApplyPreset( preset );
        }
    }

    public static void TriggerGroupFlash( string groupName, float duration = 0.15f, float brightnessMultiplier = 2f )
    {
        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.TriggerFlash( duration, brightnessMultiplier );
        }
    }

    public static void TriggerGroupAlarm( string groupName, float duration = 2f )
    {
        foreach ( var light in GetLightsInGroup( groupName ) )
        {
            light.TriggerAlarm( duration );
        }
    }

    public static void SetPowerGridState( string powerGridTag, bool isPowered )
    {
        foreach ( var light in GetLightsInPowerGrid( powerGridTag ) )
        {
            light.SetPowered( isPowered );
        }
    }

    private static IEnumerable<UltimateLightManager> GetLightsInGroup( string groupName )
    {
        return EnumerateAllLights().Where( light => string.Equals( light.LightGroup, groupName, StringComparison.OrdinalIgnoreCase ) );
    }

    private static IEnumerable<UltimateLightManager> GetLightsInPowerGrid( string powerGridTag )
    {
        return EnumerateAllLights().Where( light => string.Equals( light.PowerGridTag, powerGridTag, StringComparison.OrdinalIgnoreCase ) );
    }

    private static IEnumerable<UltimateLightManager> EnumerateAllLights()
    {
        var visitedScenes = new HashSet<Scene>();
        var visitedLights = new HashSet<UltimateLightManager>();

        foreach ( var scene in EnumerateCandidateScenes() )
        {
            if ( scene == null || !visitedScenes.Add( scene ) )
            {
                continue;
            }

            foreach ( var light in scene.GetAllComponents<UltimateLightManager>() )
            {
                if ( light != null && visitedLights.Add( light ) )
                {
                    yield return light;
                }
            }
        }
    }

    private static IEnumerable<Scene> EnumerateCandidateScenes()
    {
        if ( Game.ActiveScene != null )
        {
            yield return Game.ActiveScene;
        }

        foreach ( var scene in Scene.All )
        {
            if ( scene != null )
            {
                yield return scene;
            }
        }
    }

    [Property, Order( -1 ), Group( "Management" )]
    public string LightGroup { get; set; } = "Default";

    [Property, Group( "Management" )]
    public string PowerGridTag { get; set; } = string.Empty;

    [Property, Group( "Management" )]
    public float StartDelay { get; set; } = 0.0f;

    [Property, Group( "Management" )]
    public bool AutoDesync { get; set; } = true;

    [Property, Group( "Management" )]
    public bool ShowDebugGizmos { get; set; } = false;

    [Property, Group( "Management" )]
    public bool ForceNetworkObjectMode { get; set; } = true;

    [Property, Group( "General" ), Sync( SyncFlags.FromHost )]
    public LightTypeEnum TargetLightType { get; set; } = LightTypeEnum.Point;

    [Property, Group( "General" ), Sync( SyncFlags.FromHost )]
    public bool IsEnabled { get; set; } = true;

    [Property, Group( "General" ), Sync( SyncFlags.FromHost )]
    public Color LightColor { get; set; } = Color.White;

    [Property, Group( "General" ), Range( 0, 100 ), Sync( SyncFlags.FromHost | SyncFlags.Interpolate )]
    public float Brightness { get; set; } = 1.0f;

    [Property, Group( "General" ), Range( 0, 10 )]
    public float VolumetricBoost { get; set; } = 1.0f;

    [Property, Group( "General" )]
    public bool CastShadows { get; set; } = true;

    [Property, Group( "Presets" ), Sync( SyncFlags.FromHost )]
    public LightPreset SelectedPreset { get; set; } = LightPreset.Custom;

    [Property, Group( "Presets" ), Sync( SyncFlags.FromHost )]
    public bool AutoApplyPreset { get; set; } = true;

    [Property, Group( "Transitions" )]
    public bool EnableFade { get; set; } = false;

    [Property, Group( "Transitions" )]
    public float FadeInDuration { get; set; } = 0.2f;

    [Property, Group( "Transitions" )]
    public float FadeOutDuration { get; set; } = 0.2f;

    [Property, Group( "Audio" )]
    public SoundEvent AmbientSound { get; set; }

    [Property, Group( "Audio" )]
    public SoundEvent ToggleOnSound { get; set; }

    [Property, Group( "Audio" )]
    public SoundEvent ToggleOffSound { get; set; }

    [Property, Group( "Audio" )]
    public bool ModulateVolumeWithLight { get; set; } = true;

    [Property, Group( "Audio" )]
    public bool ModulatePitchWithLight { get; set; } = false;

    [Property, Group( "Audio" ), Range( 0, 5 )]
    public float BaseVolume { get; set; } = 1.0f;

    [Property, Group( "Audio" ), Range( 0.5f, 2f )]
    public float MinPitch { get; set; } = 0.9f;

    [Property, Group( "Audio" ), Range( 0.5f, 2f )]
    public float MaxPitch { get; set; } = 1.1f;

    [Property, Group( "Optimization" )]
    public float MaxDistance { get; set; } = 2500.0f;

    [Property, Group( "Optimization" )]
    public float ShadowMaxDistance { get; set; } = 800.0f;

    [Property, Group( "Optimization" )]
    public bool EnableCulling { get; set; } = true;

    [Property, Group( "Optimization" )]
    public bool EnableAdaptiveUpdates { get; set; } = false;

    [Property, Group( "Optimization" ), Range( 1, 120 )]
    public float NearUpdateRate { get; set; } = 60.0f;

    [Property, Group( "Optimization" ), Range( 1, 120 )]
    public float FarUpdateRate { get; set; } = 12.0f;

    [Property, Group( "Gameplay" )]
    public float DefaultAlarmDuration { get; set; } = 2.0f;

    [Property, Group( "Gameplay" ), Sync( SyncFlags.FromHost )]
    public Color AlarmColor { get; set; } = new Color( 1.0f, 0.15f, 0.1f );

    [Property, Group( "Gameplay" )]
    public float AlarmStrobeSpeed { get; set; } = 8.0f;

    [Property, Group( "Gameplay" )]
    public float AlarmBrightnessMultiplier { get; set; } = 1.5f;

    [Property, FeatureEnabled( "Fire & Candle" )]
    public bool EnableFire { get; set; } = false;

    [Property, Feature( "Fire & Candle" )]
    public float FireSpeed { get; set; } = 12.0f;

    [Property, Feature( "Fire & Candle" ), Range( 0, 1 )]
    public float FireIntensity { get; set; } = 0.3f;

    [Property, Feature( "Fire & Candle" ), Range( 0, 2 )]
    public float FireChaos { get; set; } = 1.0f;

    [Property, FeatureEnabled( "Horror Mode" )]
    public bool EnableHorror { get; set; } = false;

    [Property, Feature( "Horror Mode" )]
    public float MinFlickerDelay { get; set; } = 0.05f;

    [Property, Feature( "Horror Mode" )]
    public float MaxFlickerDelay { get; set; } = 0.4f;

    [Property, Feature( "Horror Mode" ), Range( 0, 1 )]
    public float DamageSeverity { get; set; } = 0.8f;

    [Property, Feature( "Horror Mode" )]
    public SoundEvent SparkSound { get; set; }

    [Property, FeatureEnabled( "Disco Mode" )]
    public bool EnableDisco { get; set; } = false;

    [Property, Feature( "Disco Mode" )]
    public float DiscoSpeed { get; set; } = 20.0f;

    [Property, Feature( "Disco Mode" ), Range( 0, 1 )]
    public float DiscoSaturation { get; set; } = 1.0f;

    [Property, Feature( "Disco Mode" ), Range( 0, 1 )]
    public float DiscoValue { get; set; } = 1.0f;

    [Property, FeatureEnabled( "Color Transition" )]
    public bool EnableColorTransition { get; set; } = false;

    [Property, Feature( "Color Transition" ), Sync( SyncFlags.FromHost )]
    public Color SecondaryColor { get; set; } = new Color( 0.2f, 0.85f, 1.0f );

    [Property, Feature( "Color Transition" )]
    public float ColorTransitionSpeed { get; set; } = 1.0f;

    [Property, FeatureEnabled( "Proximity Sensor" )]
    public bool EnableSensor { get; set; } = false;

    [Property, Feature( "Proximity Sensor" )]
    public float SensorRange { get; set; } = 300.0f;

    [Property, Feature( "Proximity Sensor" ), Range( 0, 1 )]
    public float SensorMinBrightness { get; set; } = 0.0f;

    [Property, Feature( "Proximity Sensor" ), Range( 0, 1 )]
    public float SensorMaxBrightness { get; set; } = 1.0f;

    [Property, Feature( "Proximity Sensor" ), Range( 1, 20 )]
    public float SensorSmoothness { get; set; } = 5.0f;

    [Property, Feature( "Proximity Sensor" )]
    public bool InvertSensor { get; set; } = false;

    [Property, FeatureEnabled( "Motion Sway" )]
    public bool EnableSway { get; set; } = false;

    [Property, Feature( "Motion Sway" )]
    public float SwaySpeedPitch { get; set; } = 1.0f;

    [Property, Feature( "Motion Sway" )]
    public float SwayAmountPitch { get; set; } = 5.0f;

    [Property, Feature( "Motion Sway" )]
    public float SwaySpeedRoll { get; set; } = 0.7f;

    [Property, Feature( "Motion Sway" )]
    public float SwayAmountRoll { get; set; } = 3.0f;

    [Property, FeatureEnabled( "Flicker Pattern" )]
    public bool EnablePattern { get; set; } = false;

    [Property, Feature( "Flicker Pattern" )]
    public string Pattern { get; set; } = "mmnmmommommnonmmonqnmmo";

    [Property, Feature( "Flicker Pattern" )]
    public float PatternSpeed { get; set; } = 10.0f;

    [Property, FeatureEnabled( "Pulse" )]
    public bool EnablePulse { get; set; } = false;

    [Property, Feature( "Pulse" )]
    public float PulseSpeed { get; set; } = 1.0f;

    [Property, Feature( "Pulse" ), Range( 0, 1 )]
    public float PulseMin { get; set; } = 0.2f;

    [Property, FeatureEnabled( "Strobe" )]
    public bool EnableStrobe { get; set; } = false;

    [Property, Feature( "Strobe" )]
    public float StrobeSpeed { get; set; } = 10.0f;

    [Property, Feature( "Strobe" ), Range( 0.1f, 0.9f )]
    public float StrobeDutyCycle { get; set; } = 0.5f;

    [Property, FeatureEnabled( "Kelvin" )]
    public bool EnableKelvin { get; set; } = false;

    [Property, Feature( "Kelvin" ), Range( 1000, 12000 )]
    public int KelvinTemperature { get; set; } = 4500;

    [Property, FeatureEnabled( "Power Surge" )]
    public bool EnablePowerSurge { get; set; } = false;

    [Property, Feature( "Power Surge" )]
    public float SurgeMinInterval { get; set; } = 4.0f;

    [Property, Feature( "Power Surge" )]
    public float SurgeMaxInterval { get; set; } = 10.0f;

    [Property, Feature( "Power Surge" )]
    public float SurgeDuration { get; set; } = 0.15f;

    [Property, Feature( "Power Surge" )]
    public float SurgeBrightnessMultiplier { get; set; } = 1.8f;

    public bool Powered => PoweredState;
    public float ExternalBrightnessMultiplier => ExternalBrightnessMultiplierState;
    public bool HasColorOverride => HasExternalColorOverrideState;
    public bool AlarmActive => AlarmEndTimeState > RealTime.Now;

    private PointLight _pointLight;
    private SpotLight _spotLight;
    private Light ActiveLight => TargetLightType == LightTypeEnum.Point ? (Light)_pointLight : _spotLight;

    private int _lastKelvin = -1;
    private Color _cachedKelvinColor = Color.White;
    private Rotation _baseRotation;
    private bool _isInitialized;
    private bool _hasAppliedPreset;
    private LightPreset _lastAppliedPreset = LightPreset.Custom;
    private LightPreset _lastObservedPreset = LightPreset.Custom;
    private bool _lastObservedAutoApplyPreset = true;
    private LightTypeEnum _lastSyncedLightType = LightTypeEnum.Point;
    private float _brokenMultiplier = 1.0f;
    private float _nextFlicker;
    private float _sensorWeightTarget = 1.0f;
    private float _sensorWeightCurrent = 1.0f;
    private float _randomTimeOffset;
    private float _creationTime;
    private float _lastUpdateTimestamp;
    private float _enabledBlend = 1.0f;
    private float _nextAdaptiveUpdateTime;
    private float _nextViewerCameraRefreshTime;
    private float _nextSurgeTime;
    private float _surgeEndTime;
    private bool _hasOutputState;
    private bool _lastOutputEnabled;
    private CameraComponent _cachedViewerCamera;
    private SoundHandle _ambientSoundHandle;

    [Sync( SyncFlags.FromHost )]
    private bool PoweredState { get; set; } = true;

    [Sync( SyncFlags.FromHost | SyncFlags.Interpolate )]
    private float ExternalBrightnessMultiplierState { get; set; } = 1.0f;

    [Sync( SyncFlags.FromHost )]
    private bool HasExternalColorOverrideState { get; set; }

    [Sync( SyncFlags.FromHost )]
    private Color ExternalColorOverrideState { get; set; } = Color.White;

    [Sync( SyncFlags.FromHost )]
    private float FlashEndTimeState { get; set; }

    [Sync( SyncFlags.FromHost )]
    private float FlashBrightnessMultiplierState { get; set; } = 1.0f;

    [Sync( SyncFlags.FromHost )]
    private float AlarmEndTimeState { get; set; }

    protected override void OnAwake()
    {
        EnsureNetworkMode();
    }

    protected override void OnStart()
    {
        EnsureNetworkMode();

        _baseRotation = LocalRotation;
        _creationTime = RealTime.Now;
        _lastUpdateTimestamp = _creationTime;
        _enabledBlend = IsEnabled ? 1.0f : 0.0f;

        if ( AutoDesync )
        {
            _randomTimeOffset = Game.Random.Float( 0f, 100f );
        }

        SyncComponentsIfNeeded( force: true );

        if ( AutoApplyPreset )
        {
            ApplyPresetInternal( SelectedPreset );
        }

        ScheduleNextSurge( _creationTime );

        _lastObservedPreset = SelectedPreset;
        _lastObservedAutoApplyPreset = AutoApplyPreset;
        _isInitialized = true;
    }

    protected override void OnUpdate()
    {
        if ( !_isInitialized )
        {
            return;
        }

        SyncComponentsIfNeeded();
        SyncPresetSelectionIfNeeded();

        var light = ActiveLight;
        if ( light == null )
        {
            return;
        }

        bool isPlaying = Game.IsPlaying;
        float absoluteTime = RealTime.Now;
        float effectTime = (isPlaying ? Time.Now : absoluteTime) + _randomTimeOffset;
        float deltaTime = GetFrameDelta( absoluteTime );

        if ( isPlaying && StartDelay > 0f && (absoluteTime - _creationTime) < StartDelay )
        {
            DisableOutput( light );
            return;
        }

        var viewerCam = GetViewerCamera( absoluteTime );
        float distSq = viewerCam != null ? WorldPosition.DistanceSquared( viewerCam.WorldPosition ) : 0f;

        if ( ShouldSkipAdaptiveUpdate( isPlaying, viewerCam, distSq, absoluteTime ) )
        {
            UpdateAmbientSoundPosition();
            return;
        }

        UpdateSensorWeight( viewerCam, distSq, deltaTime );
        UpdateEnabledBlend( deltaTime );
        UpdateSway( effectTime );

        if ( isPlaying && EnableCulling && viewerCam != null && distSq > MaxDistance * MaxDistance )
        {
            DisableOutput( light );
            return;
        }

        float fx = 1.0f;

        fx *= EvaluatePulseAndStrobe( effectTime );
        fx *= EvaluatePattern( effectTime );
        fx *= EvaluateFire( effectTime );
        fx *= EvaluateHorror( effectTime, isPlaying );
        fx *= EvaluatePowerSurge( absoluteTime );
        fx *= EvaluateAlarm( effectTime, absoluteTime );

        if ( absoluteTime < FlashEndTimeState )
        {
            fx *= FlashBrightnessMultiplierState;
        }

        float finalBrightness = Brightness * fx * _sensorWeightCurrent * _enabledBlend * ExternalBrightnessMultiplierState;
        finalBrightness = Math.Max( finalBrightness, 0f );

        bool shouldBeEnabled = finalBrightness > 0.001f;
        light.Enabled = shouldBeEnabled;

        Color resolvedColor = ResolveLightColor( effectTime, absoluteTime );
        light.LightColor = resolvedColor * finalBrightness;
        light.Shadows = CastShadows && ( !isPlaying || distSq < ShadowMaxDistance * ShadowMaxDistance );
        light.FogStrength = VolumetricBoost;

        UpdateOutputState( shouldBeEnabled, true );
        ManageAudio( shouldBeEnabled, finalBrightness / Math.Max( Brightness, 0.01f ) );
    }

    public void TurnOn()
    {
        SetEnabledState( true );
    }

    [Button]
    public void ApplySelectedPreset()
    {
        ApplyPreset( SelectedPreset );
    }

    public void TurnOff()
    {
        SetEnabledState( false );
    }

    public void Toggle()
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        IsEnabled = !IsEnabled;
    }

    public void SetPowered( bool powered )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        PoweredState = powered;
    }

    public void SetExternalBrightness( float multiplier )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        ExternalBrightnessMultiplierState = Math.Max( multiplier, 0f );
    }

    public void ResetExternalBrightness()
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        ExternalBrightnessMultiplierState = 1.0f;
    }

    public void SetColorOverride( Color color )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        HasExternalColorOverrideState = true;
        ExternalColorOverrideState = color;
    }

    public void ClearColorOverride()
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        HasExternalColorOverrideState = false;
        ExternalColorOverrideState = Color.White;
    }

    public void TriggerFlash( float duration = 0.15f, float brightnessMultiplier = 2f )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        FlashEndTimeState = RealTime.Now + Math.Max( duration, 0.01f );
        FlashBrightnessMultiplierState = Math.Max( brightnessMultiplier, 1.0f );
    }

    public void TriggerAlarm( float duration = -1f )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        if ( duration <= 0f )
        {
            duration = DefaultAlarmDuration;
        }

        AlarmEndTimeState = Math.Max( AlarmEndTimeState, RealTime.Now + duration );
    }

    public void ClearAlarm()
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        AlarmEndTimeState = 0f;
    }

    [Button]
    public void PreviewFlash()
    {
        TriggerFlash();
    }

    [Button]
    public void PreviewAlarm()
    {
        TriggerAlarm();
    }

    [Button]
    public void ResetRuntimeOverrides()
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        ClearAlarm();
        ClearColorOverride();
        ResetExternalBrightness();
        SetPowered( true );
    }

    public void ApplyPreset( LightPreset preset )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        ApplyPresetInternal( preset );
    }

    public void SetEnabledState( bool isEnabled )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        IsEnabled = isEnabled;
    }

    public void SetBrightnessLevel( float brightness )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        Brightness = Math.Max( brightness, 0f );
    }

    public void SetLightColorValue( Color color )
    {
        if ( ShouldIgnoreNetworkMutation() )
        {
            return;
        }

        LightColor = color;
    }

    private void ApplyPresetInternal( LightPreset preset )
    {
        SelectedPreset = preset;
        ResetPresetControlledFeatures();

        switch ( preset )
        {
            case LightPreset.Candle:
                Brightness = 0.75f;
                LightColor = new Color( 1.0f, 0.76f, 0.5f );
                SecondaryColor = new Color( 1.0f, 0.66f, 0.35f );
                EnableKelvin = true;
                KelvinTemperature = 1800;
                EnableFire = true;
                FireSpeed = 10.0f;
                FireIntensity = 0.18f;
                FireChaos = 0.6f;
                VolumetricBoost = 0.6f;
                break;

            case LightPreset.Torch:
                Brightness = 1.35f;
                LightColor = new Color( 1.0f, 0.72f, 0.38f );
                SecondaryColor = new Color( 1.0f, 0.45f, 0.2f );
                EnableKelvin = true;
                KelvinTemperature = 2200;
                EnableFire = true;
                FireSpeed = 13.0f;
                FireIntensity = 0.28f;
                FireChaos = 1.0f;
                VolumetricBoost = 1.4f;
                break;

            case LightPreset.Neon:
                Brightness = 1.15f;
                LightColor = new Color( 0.2f, 0.95f, 1.0f );
                SecondaryColor = new Color( 1.0f, 0.2f, 0.85f );
                EnableColorTransition = true;
                ColorTransitionSpeed = 0.65f;
                EnablePulse = true;
                PulseSpeed = 1.2f;
                PulseMin = 0.75f;
                CastShadows = false;
                VolumetricBoost = 0.25f;
                break;

            case LightPreset.Alarm:
                Brightness = 2.0f;
                LightColor = new Color( 1.0f, 0.18f, 0.12f );
                AlarmColor = LightColor;
                EnableStrobe = true;
                StrobeSpeed = 7.0f;
                StrobeDutyCycle = 0.45f;
                CastShadows = false;
                VolumetricBoost = 1.1f;
                break;

            case LightPreset.BrokenLamp:
                Brightness = 1.0f;
                LightColor = new Color( 1.0f, 0.93f, 0.82f );
                EnableHorror = true;
                MinFlickerDelay = 0.04f;
                MaxFlickerDelay = 0.25f;
                DamageSeverity = 0.85f;
                EnableKelvin = true;
                KelvinTemperature = 3400;
                break;

            case LightPreset.SciFi:
                Brightness = 1.6f;
                LightColor = new Color( 0.35f, 0.78f, 1.0f );
                SecondaryColor = new Color( 0.1f, 1.0f, 0.8f );
                EnableColorTransition = true;
                ColorTransitionSpeed = 1.15f;
                EnablePulse = true;
                PulseSpeed = 0.85f;
                PulseMin = 0.55f;
                VolumetricBoost = 2.0f;
                break;

            case LightPreset.StreetLight:
                Brightness = 1.4f;
                LightColor = new Color( 1.0f, 0.84f, 0.68f );
                EnableKelvin = true;
                KelvinTemperature = 3500;
                MaxDistance = 4500.0f;
                ShadowMaxDistance = 1200.0f;
                VolumetricBoost = 0.55f;
                break;

            case LightPreset.Custom:
            default:
                break;
        }

        _lastAppliedPreset = preset;
        _hasAppliedPreset = true;
    }

    private void ResetPresetControlledFeatures()
    {
        EnableFire = false;
        EnableHorror = false;
        EnableDisco = false;
        EnableColorTransition = false;
        EnablePulse = false;
        EnableStrobe = false;
        EnableKelvin = false;
    }

    private void SyncPresetSelectionIfNeeded()
    {
        bool autoApplyChanged = AutoApplyPreset != _lastObservedAutoApplyPreset;
        bool presetChanged = SelectedPreset != _lastObservedPreset;

        _lastObservedAutoApplyPreset = AutoApplyPreset;
        _lastObservedPreset = SelectedPreset;

        if ( !AutoApplyPreset )
        {
            return;
        }

        if ( autoApplyChanged || presetChanged || !_hasAppliedPreset || SelectedPreset != _lastAppliedPreset )
        {
            ApplyPresetInternal( SelectedPreset );
        }
    }

    private void SyncComponentsIfNeeded( bool force = false )
    {
        bool missingActiveLight = TargetLightType == LightTypeEnum.Point ? _pointLight == null : _spotLight == null;
        if ( !force && !missingActiveLight && TargetLightType == _lastSyncedLightType )
        {
            return;
        }

        Component createdComponent = null;

        if ( TargetLightType == LightTypeEnum.Point )
        {
            if ( _pointLight == null )
            {
                _pointLight = Components.GetOrCreate<PointLight>();
                createdComponent = _pointLight;
            }

            if ( _spotLight != null && _spotLight.Enabled )
            {
                _spotLight.Enabled = false;
            }
        }
        else
        {
            if ( _spotLight == null )
            {
                _spotLight = Components.GetOrCreate<SpotLight>();
                createdComponent = _spotLight;
            }

            if ( _pointLight != null && _pointLight.Enabled )
            {
                _pointLight.Enabled = false;
            }
        }

        _lastSyncedLightType = TargetLightType;

        if ( createdComponent != null && Game.IsPlaying && GameObject.NetworkMode == NetworkMode.Object )
        {
            GameObject.Network.Refresh( createdComponent );
        }
    }

    private CameraComponent GetViewerCamera( float absoluteTime )
    {
        var sceneCamera = Scene.Camera;
        if ( sceneCamera != null )
        {
            _cachedViewerCamera = sceneCamera;
            _nextViewerCameraRefreshTime = absoluteTime + 0.25f;
            return sceneCamera;
        }

        if ( _cachedViewerCamera != null && absoluteTime < _nextViewerCameraRefreshTime )
        {
            return _cachedViewerCamera;
        }

        _nextViewerCameraRefreshTime = absoluteTime + 0.25f;
        _cachedViewerCamera = Scene.GetAllComponents<CameraComponent>().FirstOrDefault( camera => camera != null && camera.Enabled );
        return _cachedViewerCamera;
    }

    private void EnsureNetworkMode()
    {
        if ( !ForceNetworkObjectMode || !Game.IsPlaying || GameObject.NetworkMode == NetworkMode.Object )
        {
            return;
        }

        GameObject.NetworkMode = NetworkMode.Object;
    }

    private bool ShouldIgnoreNetworkMutation()
    {
        return Game.IsPlaying && IsProxy;
    }

    private float GetFrameDelta( float absoluteTime )
    {
        float delta = Math.Clamp( absoluteTime - _lastUpdateTimestamp, 0.0001f, 0.25f );
        _lastUpdateTimestamp = absoluteTime;
        return delta;
    }

    private bool ShouldSkipAdaptiveUpdate( bool isPlaying, CameraComponent viewerCam, float distSq, float absoluteTime )
    {
        if ( !EnableAdaptiveUpdates || !isPlaying || viewerCam == null )
        {
            return false;
        }

        if ( absoluteTime < _nextAdaptiveUpdateTime )
        {
            return true;
        }

        float maxDistanceSq = Math.Max( MaxDistance * MaxDistance, 1f );
        float distRatio = Math.Clamp( distSq / maxDistanceSq, 0f, 1f );
        float nearInterval = 1f / Math.Max( NearUpdateRate, 1f );
        float farInterval = 1f / Math.Max( FarUpdateRate, 1f );
        _nextAdaptiveUpdateTime = absoluteTime + MathX.Lerp( nearInterval, farInterval, distRatio );
        return false;
    }

    private void UpdateSensorWeight( CameraComponent viewerCam, float distSq, float deltaTime )
    {
        if ( EnableSensor && viewerCam != null && SensorRange > 0.01f )
        {
            float distRatio = Math.Clamp( 1.0f - (MathF.Sqrt( distSq ) / SensorRange), 0f, 1f );
            float rawWeight = InvertSensor ? 1.0f - distRatio : distRatio;
            _sensorWeightTarget = MathX.Lerp( SensorMinBrightness, SensorMaxBrightness, rawWeight );
        }
        else
        {
            _sensorWeightTarget = 1.0f;
        }

        _sensorWeightCurrent = MathX.Lerp( _sensorWeightCurrent, _sensorWeightTarget, Math.Clamp( deltaTime * SensorSmoothness, 0f, 1f ) );
    }

    private void UpdateEnabledBlend( float deltaTime )
    {
        float target = IsEnabled && PoweredState ? 1.0f : 0.0f;

        if ( !EnableFade )
        {
            _enabledBlend = target;
            return;
        }

        float duration = target > _enabledBlend ? Math.Max( FadeInDuration, 0.0001f ) : Math.Max( FadeOutDuration, 0.0001f );
        float lerp = Math.Clamp( deltaTime / duration, 0f, 1f );
        _enabledBlend = MathX.Lerp( _enabledBlend, target, lerp );

        if ( Math.Abs( _enabledBlend - target ) < 0.001f )
        {
            _enabledBlend = target;
        }
    }

    private void UpdateSway( float effectTime )
    {
        if ( EnableSway )
        {
            float pitch = MathF.Sin( effectTime * SwaySpeedPitch ) * SwayAmountPitch;
            float roll = MathF.Cos( effectTime * SwaySpeedRoll ) * SwayAmountRoll;
            LocalRotation = _baseRotation * Rotation.From( pitch, 0f, roll );
            return;
        }

        LocalRotation = _baseRotation;
    }

    private float EvaluatePulseAndStrobe( float effectTime )
    {
        if ( EnableStrobe )
        {
            float cycle = (effectTime * StrobeSpeed) % 1.0f;
            return cycle < StrobeDutyCycle ? 1.0f : 0.0f;
        }

        if ( EnablePulse )
        {
            float sine = (MathF.Sin( effectTime * PulseSpeed * 2.0f ) + 1.0f) * 0.5f;
            return MathX.Lerp( PulseMin, 1.0f, sine );
        }

        return 1.0f;
    }

    private float EvaluatePattern( float effectTime )
    {
        if ( !EnablePattern || string.IsNullOrWhiteSpace( Pattern ) )
        {
            return 1.0f;
        }

        int index = (int)(effectTime * PatternSpeed) % Pattern.Length;
        float value = Math.Max( 0, (char.ToLower( Pattern[index] ) - 'a') / 12.0f );
        return value;
    }

    private float EvaluateFire( float effectTime )
    {
        if ( !EnableFire )
        {
            return 1.0f;
        }

        float noise = MathF.Sin( effectTime * FireSpeed ) + MathF.Sin( effectTime * FireSpeed * 0.5f );

        if ( FireChaos > 0f )
        {
            noise += MathF.Sin( effectTime * FireSpeed * 1.5f ) * FireChaos;
        }

        return 1.0f - (noise * 0.15f * FireIntensity);
    }

    private float EvaluateHorror( float effectTime, bool isPlaying )
    {
        if ( !EnableHorror )
        {
            return 1.0f;
        }

        if ( effectTime > _nextFlicker )
        {
            bool isDamaged = Game.Random.Float( 0f, 1f ) < DamageSeverity;
            _brokenMultiplier = isDamaged ? Game.Random.Float( 0.0f, 0.4f ) : 1.0f;
            _nextFlicker = effectTime + Game.Random.Float( MinFlickerDelay, MaxFlickerDelay );

            if ( isPlaying && isDamaged && SparkSound != null && _brokenMultiplier < 0.2f )
            {
                Sound.Play( SparkSound, WorldPosition );
            }
        }

        return _brokenMultiplier;
    }

    private float EvaluatePowerSurge( float absoluteTime )
    {
        if ( !EnablePowerSurge )
        {
            return 1.0f;
        }

        if ( _nextSurgeTime <= 0f )
        {
            ScheduleNextSurge( absoluteTime );
        }

        if ( absoluteTime >= _nextSurgeTime )
        {
            _surgeEndTime = absoluteTime + Math.Max( SurgeDuration, 0.01f );
            ScheduleNextSurge( _surgeEndTime );
        }

        return absoluteTime < _surgeEndTime ? Math.Max( SurgeBrightnessMultiplier, 1.0f ) : 1.0f;
    }

    private float EvaluateAlarm( float effectTime, float absoluteTime )
    {
        if ( absoluteTime >= AlarmEndTimeState )
        {
            return 1.0f;
        }

        float cycle = (effectTime * AlarmStrobeSpeed) % 1.0f;
        float gate = cycle < 0.5f ? 1.0f : 0.15f;
        return gate * Math.Max( AlarmBrightnessMultiplier, 0f );
    }

    private void ScheduleNextSurge( float absoluteTime )
    {
        float minInterval = Math.Min( SurgeMinInterval, SurgeMaxInterval );
        float maxInterval = Math.Max( SurgeMinInterval, SurgeMaxInterval );
        _nextSurgeTime = absoluteTime + Game.Random.Float( Math.Max( minInterval, 0.01f ), Math.Max( maxInterval, 0.01f ) );
    }

    private Color ResolveLightColor( float effectTime, float absoluteTime )
    {
        Color color = LightColor;

        if ( EnableKelvin )
        {
            if ( KelvinTemperature != _lastKelvin )
            {
                _cachedKelvinColor = KelvinToColor( KelvinTemperature );
                _lastKelvin = KelvinTemperature;
            }

            color = _cachedKelvinColor;
        }

        if ( EnableColorTransition )
        {
            float lerp = (MathF.Sin( effectTime * ColorTransitionSpeed ) + 1.0f) * 0.5f;
            color = Color.Lerp( color, SecondaryColor, lerp, true );
        }

        if ( EnableDisco )
        {
            color = new ColorHsv( (effectTime * DiscoSpeed) % 360f, DiscoSaturation, DiscoValue ).ToColor();
        }

        if ( absoluteTime < AlarmEndTimeState )
        {
            color = Color.Lerp( color, AlarmColor, 0.85f, true );
        }

        if ( HasExternalColorOverrideState )
        {
            color = ExternalColorOverrideState;
        }

        return color;
    }

    private void UpdateOutputState( bool shouldBeEnabled, bool playOneShot )
    {
        if ( !_hasOutputState )
        {
            _hasOutputState = true;
            _lastOutputEnabled = shouldBeEnabled;
            return;
        }

        if ( _lastOutputEnabled == shouldBeEnabled )
        {
            return;
        }

        if ( playOneShot && Game.IsPlaying )
        {
            if ( shouldBeEnabled && ToggleOnSound != null )
            {
                Sound.Play( ToggleOnSound, WorldPosition );
            }
            else if ( !shouldBeEnabled && ToggleOffSound != null )
            {
                Sound.Play( ToggleOffSound, WorldPosition );
            }
        }

        _lastOutputEnabled = shouldBeEnabled;
    }

    private void DisableOutput( Light light )
    {
        light.Enabled = false;
        UpdateOutputState( false, false );
        ManageAudio( false, 0f );
    }

    private void UpdateAmbientSoundPosition()
    {
        if ( _ambientSoundHandle != null && !_ambientSoundHandle.IsStopped )
        {
            _ambientSoundHandle.Position = WorldPosition;
        }
    }

    private void ManageAudio( bool isLightEnabled, float intensityRatio )
    {
        if ( !Game.IsPlaying || AmbientSound == null )
        {
            return;
        }

        if ( isLightEnabled )
        {
            if ( _ambientSoundHandle == null || _ambientSoundHandle.IsStopped )
            {
                _ambientSoundHandle = Sound.Play( AmbientSound, WorldPosition );
            }

            if ( _ambientSoundHandle != null )
            {
                _ambientSoundHandle.Position = WorldPosition;
                _ambientSoundHandle.Volume = BaseVolume * (ModulateVolumeWithLight ? intensityRatio : 1.0f);
                _ambientSoundHandle.Pitch = ModulatePitchWithLight ? MathX.Lerp( MinPitch, MaxPitch, intensityRatio ) : 1.0f;
            }
        }
        else if ( _ambientSoundHandle != null )
        {
            _ambientSoundHandle.Stop();
            _ambientSoundHandle = null;
        }
    }

    private Color KelvinToColor( int kelvin )
    {
        float temperature = kelvin / 100.0f;
        float red;
        float green;
        float blue;

        if ( temperature <= 66f )
        {
            red = 255f;
            green = Math.Clamp( 99.47f * MathF.Log( temperature ) - 161.11f, 0f, 255f );
        }
        else
        {
            red = Math.Clamp( 329.698f * MathF.Pow( temperature - 60f, -0.133f ), 0f, 255f );
            green = Math.Clamp( 288.12f * MathF.Pow( temperature - 60f, -0.075f ), 0f, 255f );
        }

        if ( temperature >= 66f )
        {
            blue = 255f;
        }
        else if ( temperature <= 19f )
        {
            blue = 0f;
        }
        else
        {
            blue = Math.Clamp( 138.51f * MathF.Log( temperature - 10f ) - 305.04f, 0f, 255f );
        }

        return new Color( red / 255f, green / 255f, blue / 255f );
    }

    protected override void DrawGizmos()
    {
        if ( !ShowDebugGizmos )
        {
            return;
        }

        Gizmo.Draw.Text( $"Group: {LightGroup}", new Transform( Vector3.Up * 20f ), size: 12 );

        if ( !string.IsNullOrWhiteSpace( PowerGridTag ) )
        {
            Gizmo.Draw.Text( $"Grid: {PowerGridTag}", new Transform( Vector3.Up * 34f ), size: 12 );
        }

        if ( EnableSensor )
        {
            Gizmo.Draw.Color = Color.Cyan.WithAlpha( 0.2f );
            Gizmo.Draw.SolidSphere( Vector3.Zero, SensorRange );
            Gizmo.Draw.Color = Color.Cyan;
            Gizmo.Draw.LineSphere( Vector3.Zero, SensorRange );
        }

        if ( EnableCulling )
        {
            Gizmo.Draw.Color = Color.Red.WithAlpha( 0.05f );
            Gizmo.Draw.LineSphere( Vector3.Zero, MaxDistance );
            Gizmo.Draw.Text( $"Cull: {MaxDistance}", new Transform( Vector3.Up * (MaxDistance * 0.9f) ), size: 14 );
        }
    }

    protected override void OnDestroy()
    {
        if ( _ambientSoundHandle != null )
        {
            _ambientSoundHandle.Stop();
        }
    }
}