Editor window for the Ultimate Light Manager builder. It renders UI to pick, save, delete and apply built-in or custom light presets, persist builder state to project settings, capture settings from selected scene objects, and create configured UltimateLightManager components on new GameObjects.
using Sandbox;
using global::Editor;
using Dreams.UltimateLightManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Dreams.UltimateLightManager.Editor;
internal sealed class UltimateLightManagerBuilderWindow : Window
{
private const string StatePath = "ultimate_light_manager_builder.json";
private static UltimateLightManagerBuilderWindow Instance;
private readonly UltimateLightManagerBuilderState _state;
private readonly UltimateLightManagerBuilderDraft _draft;
private readonly SerializedObject _draftObject;
private readonly List<PresetEntry> _presetEntries = new();
private ComboBox _presetCombo;
private bool _isUpdatingPresetCombo;
private UltimateLightManagerBuilderWindow()
{
Parent = EditorWindow;
WindowFlags = WindowFlags.Dialog | WindowFlags.Customized | WindowFlags.CloseButton | WindowFlags.WindowSystemMenuHint | WindowFlags.WindowTitle;
DeleteOnClose = true;
WindowTitle = "Ultimate Light Builder";
Size = new Vector2( 520, 760 );
MinimumSize = new Vector2( 430, 620 );
StatusBar.Visible = false;
SetWindowIcon( "tungsten" );
_state = LoadState();
_draft = _state.Draft ?? new UltimateLightManagerBuilderDraft();
_state.Draft = _draft;
_draftObject = _draft.GetSerialized();
_draftObject.OnPropertyChanged += OnDraftPropertyChanged;
BuildUi();
Show();
}
protected override void OnClosed()
{
if ( ReferenceEquals( Instance, this ) )
{
Instance = null;
}
base.OnClosed();
}
private void BuildUi()
{
Canvas = new Widget( this );
Canvas.OnPaintOverride = () =>
{
Paint.ClearPen();
Paint.SetBrush( Theme.WidgetBackground );
Paint.DrawRect( Canvas.LocalRect );
return true;
};
Canvas.Layout = Layout.Column();
Canvas.Layout.Margin = 8;
Canvas.Layout.Spacing = 8;
var header = Canvas.Layout.AddColumn();
header.Spacing = 4;
header.Add( new Label( "Ultimate Light Builder", this ) )
.SetStyles( "font-size: 18px; font-weight: bold;" );
var subtitle = header.Add( new Label( "Creer rapidement une point light ou une spot light deja prete avec Ultimate Light Manager.", this ) );
subtitle.WordWrap = true;
subtitle.SetStyles( $"color: {Theme.Text.WithAlpha( 0.7f ).Hex};" );
Canvas.Layout.AddSeparator();
var presetSection = Canvas.Layout.AddColumn();
presetSection.Spacing = 6;
var presetRow = presetSection.AddRow();
presetRow.Spacing = 6;
presetRow.Add( new Label( "Charger preset", this ) { FixedWidth = 110, Alignment = TextFlag.LeftCenter } );
_presetCombo = presetRow.Add( new ComboBox( this ), 1 );
_presetCombo.ItemChanged += OnPresetSelectionChanged;
_presetCombo.ToolTip = "Charge un preset builtin ou un preset custom.";
presetRow.Add( new Button( "Depuis la selection", this )
{
Icon = "south_west",
Clicked = LoadFromSelection
} );
var actionRow = presetSection.AddRow();
actionRow.Spacing = 6;
actionRow.Add( new Button( "Enregistrer preset", this )
{
Icon = "save",
Clicked = OpenSavePresetDialog
}, 1 );
actionRow.Add( new Button( "Supprimer preset", this )
{
Icon = "delete",
Clicked = ConfirmDeleteSelectedPreset
}, 1 );
actionRow.Add( new Button( "Reinitialiser", this )
{
Icon = "restart_alt",
Clicked = ResetDraft
}, 1 );
RebuildPresetCombo();
Canvas.Layout.AddSeparator();
var scroll = Canvas.Layout.Add( new ScrollArea( this ), 1 );
scroll.Canvas = new Widget( scroll );
scroll.Canvas.Layout = Layout.Column();
scroll.Canvas.Layout.Spacing = 6;
scroll.Canvas.Layout.Margin = 0;
var sheet = new ControlSheet();
sheet.AddObject( _draftObject );
scroll.Canvas.Layout.Add( sheet );
scroll.Canvas.Layout.AddStretchCell();
Canvas.Layout.AddSeparator();
var footer = Canvas.Layout.AddColumn();
footer.Spacing = 6;
var help = footer.Add( new Label( "Les presets custom enregistrent les reglages de base du builder. Les presets builtin gardent leurs effets speciaux a la creation.", this ) );
help.WordWrap = true;
help.SetStyles( $"color: {Theme.Text.WithAlpha( 0.7f ).Hex};" );
footer.Add( new Button.Primary( "Creer la light", "add_circle" )
{
Clicked = CreateLightFromDraft
} );
}
private void OnDraftPropertyChanged( SerializedProperty property )
{
SaveState();
}
private void OnPresetSelectionChanged()
{
if ( _isUpdatingPresetCombo )
{
return;
}
int index = _presetCombo?.CurrentIndex ?? -1;
if ( index < 0 || index >= _presetEntries.Count )
{
return;
}
var entry = _presetEntries[index];
_state.SelectedPresetKey = entry.Key;
if ( entry.IsCustom )
{
_draft.ApplyPresetData( entry.CustomPreset.Data );
}
else
{
ApplyBuiltInPresetToDraft( _draft, entry.BuiltInPreset );
}
SaveState();
BuildUi();
}
private void OpenSavePresetDialog()
{
Dialog.AskString( SavePreset, "Nom du preset :", okay: "Enregistrer", title: "Enregistrer un preset", minLength: 1 );
}
private void SavePreset( string presetName )
{
presetName = presetName?.Trim();
if ( string.IsNullOrWhiteSpace( presetName ) )
{
return;
}
var existing = _state.CustomPresets.FirstOrDefault( x => string.Equals( x.Name, presetName, StringComparison.OrdinalIgnoreCase ) );
if ( existing == null )
{
existing = new UltimateLightManagerCustomPreset
{
Name = presetName
};
_state.CustomPresets.Add( existing );
}
existing.Name = presetName;
existing.Data = _draft.ToPresetData();
_state.SelectedPresetKey = GetCustomPresetKey( presetName );
SaveState();
BuildUi();
}
private void ConfirmDeleteSelectedPreset()
{
var selectedPreset = GetSelectedCustomPreset();
if ( selectedPreset == null )
{
return;
}
Dialog.AskConfirm(
() => DeletePreset( selectedPreset ),
$"Supprimer le preset \"{selectedPreset.Name}\" ?",
"Supprimer le preset",
"Supprimer",
"Annuler"
);
}
private void DeletePreset( UltimateLightManagerCustomPreset preset )
{
_state.CustomPresets.Remove( preset );
_state.SelectedPresetKey = GetBuiltInPresetKey( UltimateLightManager.LightPreset.Custom );
SaveState();
BuildUi();
}
private void ResetDraft()
{
string lightName = _draft.LightName;
var lightType = _draft.TargetLightType;
_draft.ApplyPresetData( new UltimateLightManagerPresetData() );
_draft.LightName = lightName;
_draft.TargetLightType = lightType;
_state.SelectedPresetKey = GetBuiltInPresetKey( UltimateLightManager.LightPreset.Custom );
SaveState();
BuildUi();
}
private void LoadFromSelection()
{
var selectedLight = GetSelectedLightManager();
if ( selectedLight == null )
{
return;
}
_draft.LightName = string.IsNullOrWhiteSpace( selectedLight.GameObject?.Name ) ? _draft.LightName : selectedLight.GameObject.Name;
_draft.CaptureFromLight( selectedLight );
_state.SelectedPresetKey = GetBuiltInPresetKey( selectedLight.SelectedPreset );
SaveState();
BuildUi();
}
private void CreateLightFromDraft()
{
var session = SceneEditorSession.Active;
if ( session?.Scene == null )
{
return;
}
using var scope = SceneEditorSession.Scope();
using ( session.UndoScope( $"Create {_draft.GetSafeLightName()}" ).WithGameObjectCreations().WithComponentCreations().Push() )
{
var go = new GameObject( true, _draft.GetSafeLightName() );
var parent = GetSelectedParentGameObject();
if ( parent != null )
{
go.WorldTransform = parent.WorldTransform;
go.SetParent( parent, true );
}
var light = go.GetOrAddComponent<UltimateLightManager>( true );
ApplyDraftToLight( _draft, light );
EditorScene.Selection.Clear();
EditorScene.Selection.Add( go );
}
SaveState();
}
private void RebuildPresetCombo()
{
if ( _presetCombo == null )
{
return;
}
_isUpdatingPresetCombo = true;
_presetEntries.Clear();
_presetCombo.Clear();
AddBuiltInPreset( UltimateLightManager.LightPreset.Custom, "Builtin - Custom" );
AddBuiltInPreset( UltimateLightManager.LightPreset.Candle, "Builtin - Candle" );
AddBuiltInPreset( UltimateLightManager.LightPreset.Torch, "Builtin - Torch" );
AddBuiltInPreset( UltimateLightManager.LightPreset.Neon, "Builtin - Neon" );
AddBuiltInPreset( UltimateLightManager.LightPreset.Alarm, "Builtin - Alarm" );
AddBuiltInPreset( UltimateLightManager.LightPreset.BrokenLamp, "Builtin - Broken Lamp" );
AddBuiltInPreset( UltimateLightManager.LightPreset.SciFi, "Builtin - Sci-Fi" );
AddBuiltInPreset( UltimateLightManager.LightPreset.StreetLight, "Builtin - Street Light" );
foreach ( var preset in _state.CustomPresets
.Where( x => !string.IsNullOrWhiteSpace( x?.Name ) )
.OrderBy( x => x.Name, StringComparer.OrdinalIgnoreCase ) )
{
_presetEntries.Add( PresetEntry.ForCustom( preset ) );
_presetCombo.AddItem( $"Custom - {preset.Name}" );
}
string selectedKey = _state.SelectedPresetKey;
if ( string.IsNullOrWhiteSpace( selectedKey ) )
{
selectedKey = GetBuiltInPresetKey( UltimateLightManager.LightPreset.Custom );
_state.SelectedPresetKey = selectedKey;
}
int selectedIndex = _presetEntries.FindIndex( x => x.Key == selectedKey );
_presetCombo.CurrentIndex = selectedIndex >= 0 ? selectedIndex : 0;
_isUpdatingPresetCombo = false;
}
private void AddBuiltInPreset( UltimateLightManager.LightPreset preset, string label )
{
_presetEntries.Add( PresetEntry.ForBuiltIn( preset ) );
_presetCombo.AddItem( label );
}
private UltimateLightManagerCustomPreset GetSelectedCustomPreset()
{
int index = _presetCombo?.CurrentIndex ?? -1;
if ( index < 0 || index >= _presetEntries.Count )
{
return null;
}
return _presetEntries[index].IsCustom ? _presetEntries[index].CustomPreset : null;
}
private static void ApplyBuiltInPresetToDraft( UltimateLightManagerBuilderDraft draft, UltimateLightManager.LightPreset preset )
{
draft.SelectedPreset = preset;
draft.AutoApplyPreset = false;
if ( preset == UltimateLightManager.LightPreset.Custom )
{
return;
}
ResetDraftLookSettings( draft );
switch ( preset )
{
case UltimateLightManager.LightPreset.Candle:
draft.Brightness = 0.75f;
draft.LightColor = new Color( 1.0f, 0.76f, 0.5f );
draft.SecondaryColor = new Color( 1.0f, 0.66f, 0.35f );
draft.EnableKelvin = true;
draft.KelvinTemperature = 1800;
draft.EnableFire = true;
draft.FireSpeed = 10.0f;
draft.FireIntensity = 0.18f;
draft.FireChaos = 0.6f;
draft.VolumetricBoost = 0.6f;
break;
case UltimateLightManager.LightPreset.Torch:
draft.Brightness = 1.35f;
draft.LightColor = new Color( 1.0f, 0.72f, 0.38f );
draft.SecondaryColor = new Color( 1.0f, 0.45f, 0.2f );
draft.EnableKelvin = true;
draft.KelvinTemperature = 2200;
draft.EnableFire = true;
draft.FireSpeed = 13.0f;
draft.FireIntensity = 0.28f;
draft.FireChaos = 1.0f;
draft.VolumetricBoost = 1.4f;
break;
case UltimateLightManager.LightPreset.Neon:
draft.Brightness = 1.15f;
draft.LightColor = new Color( 0.2f, 0.95f, 1.0f );
draft.SecondaryColor = new Color( 1.0f, 0.2f, 0.85f );
draft.EnableColorTransition = true;
draft.ColorTransitionSpeed = 0.65f;
draft.EnablePulse = true;
draft.PulseSpeed = 1.2f;
draft.PulseMin = 0.75f;
draft.CastShadows = false;
draft.VolumetricBoost = 0.25f;
break;
case UltimateLightManager.LightPreset.Alarm:
draft.Brightness = 2.0f;
draft.LightColor = new Color( 1.0f, 0.18f, 0.12f );
draft.AlarmColor = draft.LightColor;
draft.EnableStrobe = true;
draft.StrobeSpeed = 7.0f;
draft.StrobeDutyCycle = 0.45f;
draft.CastShadows = false;
draft.VolumetricBoost = 1.1f;
break;
case UltimateLightManager.LightPreset.BrokenLamp:
draft.Brightness = 1.0f;
draft.LightColor = new Color( 1.0f, 0.93f, 0.82f );
draft.EnableHorror = true;
draft.MinFlickerDelay = 0.04f;
draft.MaxFlickerDelay = 0.25f;
draft.DamageSeverity = 0.85f;
draft.EnableKelvin = true;
draft.KelvinTemperature = 3400;
break;
case UltimateLightManager.LightPreset.SciFi:
draft.Brightness = 1.6f;
draft.LightColor = new Color( 0.35f, 0.78f, 1.0f );
draft.SecondaryColor = new Color( 0.1f, 1.0f, 0.8f );
draft.EnableColorTransition = true;
draft.ColorTransitionSpeed = 1.15f;
draft.EnablePulse = true;
draft.PulseSpeed = 0.85f;
draft.PulseMin = 0.55f;
draft.VolumetricBoost = 2.0f;
break;
case UltimateLightManager.LightPreset.StreetLight:
draft.Brightness = 1.4f;
draft.LightColor = new Color( 1.0f, 0.84f, 0.68f );
draft.EnableKelvin = true;
draft.KelvinTemperature = 3500;
draft.MaxDistance = 4500.0f;
draft.ShadowMaxDistance = 1200.0f;
draft.VolumetricBoost = 0.55f;
break;
case UltimateLightManager.LightPreset.Custom:
default:
break;
}
}
private static void ResetDraftLookSettings( UltimateLightManagerBuilderDraft draft )
{
var defaults = new UltimateLightManagerPresetData();
draft.LightColor = defaults.LightColor;
draft.SecondaryColor = defaults.SecondaryColor;
draft.Brightness = defaults.Brightness;
draft.VolumetricBoost = defaults.VolumetricBoost;
draft.CastShadows = defaults.CastShadows;
draft.EnableKelvin = defaults.EnableKelvin;
draft.KelvinTemperature = defaults.KelvinTemperature;
draft.EnableFire = defaults.EnableFire;
draft.FireSpeed = defaults.FireSpeed;
draft.FireIntensity = defaults.FireIntensity;
draft.FireChaos = defaults.FireChaos;
draft.EnableHorror = defaults.EnableHorror;
draft.MinFlickerDelay = defaults.MinFlickerDelay;
draft.MaxFlickerDelay = defaults.MaxFlickerDelay;
draft.DamageSeverity = defaults.DamageSeverity;
draft.EnableColorTransition = defaults.EnableColorTransition;
draft.ColorTransitionSpeed = defaults.ColorTransitionSpeed;
draft.EnablePulse = defaults.EnablePulse;
draft.PulseSpeed = defaults.PulseSpeed;
draft.PulseMin = defaults.PulseMin;
draft.EnableStrobe = defaults.EnableStrobe;
draft.StrobeSpeed = defaults.StrobeSpeed;
draft.StrobeDutyCycle = defaults.StrobeDutyCycle;
draft.AlarmColor = defaults.AlarmColor;
draft.MaxDistance = defaults.MaxDistance;
draft.ShadowMaxDistance = defaults.ShadowMaxDistance;
}
private static void ApplyDraftToLight( UltimateLightManagerBuilderDraft draft, UltimateLightManager light )
{
draft.ApplyToLight( light );
}
private static UltimateLightManager GetSelectedLightManager()
{
foreach ( var light in EditorScene.Selection.OfType<UltimateLightManager>() )
{
if ( light != null )
{
return light;
}
}
foreach ( var component in EditorScene.Selection.OfType<Component>() )
{
if ( component is UltimateLightManager light )
{
return light;
}
}
foreach ( var go in EditorScene.Selection.OfType<GameObject>() )
{
if ( go == null || go is Scene )
{
continue;
}
var light = go.Components
.GetAll<UltimateLightManager>( FindMode.InSelf | FindMode.InDescendants | FindMode.Enabled | FindMode.Disabled )
.FirstOrDefault();
if ( light != null )
{
return light;
}
}
return null;
}
private static GameObject GetSelectedParentGameObject()
{
foreach ( var go in EditorScene.Selection.OfType<GameObject>() )
{
if ( go != null && go is not Scene )
{
return go;
}
}
foreach ( var component in EditorScene.Selection.OfType<Component>() )
{
if ( component?.GameObject != null && component.GameObject is not Scene )
{
return component.GameObject;
}
}
return null;
}
private void SaveState()
{
_state.Draft = _draft;
global::Editor.FileSystem.ProjectSettings.WriteJson( StatePath, _state );
}
private static UltimateLightManagerBuilderState LoadState()
{
var loaded = global::Editor.FileSystem.ProjectSettings.ReadJsonOrDefault<UltimateLightManagerBuilderState>( StatePath ) ?? new UltimateLightManagerBuilderState();
UltimateLightManagerToolStateMigration.ApplyDefaults( loaded );
loaded.Draft ??= new UltimateLightManagerBuilderDraft();
loaded.CustomPresets ??= new List<UltimateLightManagerCustomPreset>();
loaded.CustomPresets = loaded.CustomPresets
.Where( x => x != null && !string.IsNullOrWhiteSpace( x.Name ) )
.GroupBy( x => x.Name, StringComparer.OrdinalIgnoreCase )
.Select( x => x.Last() )
.ToList();
loaded.SelectedPresetKey ??= GetBuiltInPresetKey( UltimateLightManager.LightPreset.Custom );
foreach ( var preset in loaded.CustomPresets )
{
preset.Data ??= new UltimateLightManagerPresetData();
}
return loaded;
}
private static string GetBuiltInPresetKey( UltimateLightManager.LightPreset preset )
{
return $"builtin:{preset}";
}
private static string GetCustomPresetKey( string presetName )
{
return $"custom:{presetName?.Trim()}";
}
private sealed class PresetEntry
{
public string Key { get; init; }
public bool IsCustom { get; init; }
public UltimateLightManager.LightPreset BuiltInPreset { get; init; }
public UltimateLightManagerCustomPreset CustomPreset { get; init; }
public static PresetEntry ForBuiltIn( UltimateLightManager.LightPreset preset )
{
return new PresetEntry
{
Key = GetBuiltInPresetKey( preset ),
BuiltInPreset = preset
};
}
public static PresetEntry ForCustom( UltimateLightManagerCustomPreset preset )
{
return new PresetEntry
{
Key = GetCustomPresetKey( preset.Name ),
IsCustom = true,
CustomPreset = preset
};
}
}
}
public sealed class UltimateLightManagerBuilderState
{
public UltimateLightManagerBuilderDraft Draft { get; set; } = new();
public List<UltimateLightManagerCustomPreset> CustomPresets { get; set; } = new();
public string SelectedPresetKey { get; set; } = string.Empty;
public string LoadedPresetKey { get; set; } = string.Empty;
public UltimateLightManagerToolLanguage ToolLanguage { get; set; } = UltimateLightManagerToolLanguage.French;
public bool ShowHelpTexts { get; set; } = true;
public bool ShowShortcutSection { get; set; } = true;
public int ToolSettingsVersion { get; set; } = 2;
}
public sealed class UltimateLightManagerCustomPreset
{
public string Name { get; set; } = string.Empty;
public UltimateLightManagerPresetData Data { get; set; } = new();
}
public class UltimateLightManagerPresetData
{
private static readonly PropertyInfo[] ComparisonProperties = typeof( UltimateLightManagerPresetData )
.GetProperties( BindingFlags.Instance | BindingFlags.Public )
.Where( property => property.CanRead && property.CanWrite )
.ToArray();
[Property, Group( "Creation" ), Order( -10 ), Title( "Light Type" )]
public UltimateLightManager.LightTypeEnum TargetLightType { get; set; } = UltimateLightManager.LightTypeEnum.Point;
[Property, Group( "Preset" ), Order( -10 ), Title( "Builtin Preset" )]
public UltimateLightManager.LightPreset SelectedPreset { get; set; } = UltimateLightManager.LightPreset.Custom;
[Property, Group( "Preset" )]
public bool AutoApplyPreset { get; set; } = true;
[Property, 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( "General" )]
public bool IsEnabled { get; set; } = true;
[Property, Group( "General" )]
public Color LightColor { get; set; } = Color.White;
[Property, Group( "General" ), Range( 0, 100 )]
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( "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" )]
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" )]
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 virtual void ApplyPresetData( UltimateLightManagerPresetData data )
{
if ( data == null )
{
return;
}
TargetLightType = data.TargetLightType;
SelectedPreset = data.SelectedPreset;
AutoApplyPreset = data.AutoApplyPreset;
LightGroup = data.LightGroup ?? "Default";
PowerGridTag = data.PowerGridTag ?? string.Empty;
StartDelay = data.StartDelay;
AutoDesync = data.AutoDesync;
ShowDebugGizmos = data.ShowDebugGizmos;
IsEnabled = data.IsEnabled;
LightColor = data.LightColor;
Brightness = data.Brightness;
VolumetricBoost = data.VolumetricBoost;
CastShadows = data.CastShadows;
EnableFade = data.EnableFade;
FadeInDuration = data.FadeInDuration;
FadeOutDuration = data.FadeOutDuration;
AmbientSound = data.AmbientSound;
ToggleOnSound = data.ToggleOnSound;
ToggleOffSound = data.ToggleOffSound;
ModulateVolumeWithLight = data.ModulateVolumeWithLight;
ModulatePitchWithLight = data.ModulatePitchWithLight;
BaseVolume = data.BaseVolume;
MinPitch = data.MinPitch;
MaxPitch = data.MaxPitch;
MaxDistance = data.MaxDistance;
ShadowMaxDistance = data.ShadowMaxDistance;
EnableCulling = data.EnableCulling;
EnableAdaptiveUpdates = data.EnableAdaptiveUpdates;
NearUpdateRate = data.NearUpdateRate;
FarUpdateRate = data.FarUpdateRate;
DefaultAlarmDuration = data.DefaultAlarmDuration;
AlarmColor = data.AlarmColor;
AlarmStrobeSpeed = data.AlarmStrobeSpeed;
AlarmBrightnessMultiplier = data.AlarmBrightnessMultiplier;
EnableFire = data.EnableFire;
FireSpeed = data.FireSpeed;
FireIntensity = data.FireIntensity;
FireChaos = data.FireChaos;
EnableHorror = data.EnableHorror;
MinFlickerDelay = data.MinFlickerDelay;
MaxFlickerDelay = data.MaxFlickerDelay;
DamageSeverity = data.DamageSeverity;
SparkSound = data.SparkSound;
EnableDisco = data.EnableDisco;
DiscoSpeed = data.DiscoSpeed;
DiscoSaturation = data.DiscoSaturation;
DiscoValue = data.DiscoValue;
EnableColorTransition = data.EnableColorTransition;
SecondaryColor = data.SecondaryColor;
ColorTransitionSpeed = data.ColorTransitionSpeed;
EnableSensor = data.EnableSensor;
SensorRange = data.SensorRange;
SensorMinBrightness = data.SensorMinBrightness;
SensorMaxBrightness = data.SensorMaxBrightness;
SensorSmoothness = data.SensorSmoothness;
InvertSensor = data.InvertSensor;
EnableSway = data.EnableSway;
SwaySpeedPitch = data.SwaySpeedPitch;
SwayAmountPitch = data.SwayAmountPitch;
SwaySpeedRoll = data.SwaySpeedRoll;
SwayAmountRoll = data.SwayAmountRoll;
EnablePattern = data.EnablePattern;
Pattern = data.Pattern ?? string.Empty;
PatternSpeed = data.PatternSpeed;
EnablePulse = data.EnablePulse;
PulseSpeed = data.PulseSpeed;
PulseMin = data.PulseMin;
EnableStrobe = data.EnableStrobe;
StrobeSpeed = data.StrobeSpeed;
StrobeDutyCycle = data.StrobeDutyCycle;
EnableKelvin = data.EnableKelvin;
KelvinTemperature = data.KelvinTemperature;
EnablePowerSurge = data.EnablePowerSurge;
SurgeMinInterval = data.SurgeMinInterval;
SurgeMaxInterval = data.SurgeMaxInterval;
SurgeDuration = data.SurgeDuration;
SurgeBrightnessMultiplier = data.SurgeBrightnessMultiplier;
}
public virtual void CaptureFromLight( UltimateLightManager light )
{
if ( light == null )
{
return;
}
TargetLightType = light.TargetLightType;
SelectedPreset = light.SelectedPreset;
AutoApplyPreset = light.AutoApplyPreset;
LightGroup = light.LightGroup;
PowerGridTag = light.PowerGridTag;
StartDelay = light.StartDelay;
AutoDesync = light.AutoDesync;
ShowDebugGizmos = light.ShowDebugGizmos;
IsEnabled = light.IsEnabled;
LightColor = light.LightColor;
Brightness = light.Brightness;
VolumetricBoost = light.VolumetricBoost;
CastShadows = light.CastShadows;
EnableFade = light.EnableFade;
FadeInDuration = light.FadeInDuration;
FadeOutDuration = light.FadeOutDuration;
AmbientSound = light.AmbientSound;
ToggleOnSound = light.ToggleOnSound;
ToggleOffSound = light.ToggleOffSound;
ModulateVolumeWithLight = light.ModulateVolumeWithLight;
ModulatePitchWithLight = light.ModulatePitchWithLight;
BaseVolume = light.BaseVolume;
MinPitch = light.MinPitch;
MaxPitch = light.MaxPitch;
MaxDistance = light.MaxDistance;
ShadowMaxDistance = light.ShadowMaxDistance;
EnableCulling = light.EnableCulling;
EnableAdaptiveUpdates = light.EnableAdaptiveUpdates;
NearUpdateRate = light.NearUpdateRate;
FarUpdateRate = light.FarUpdateRate;
DefaultAlarmDuration = light.DefaultAlarmDuration;
AlarmColor = light.AlarmColor;
AlarmStrobeSpeed = light.AlarmStrobeSpeed;
AlarmBrightnessMultiplier = light.AlarmBrightnessMultiplier;
EnableFire = light.EnableFire;
FireSpeed = light.FireSpeed;
FireIntensity = light.FireIntensity;
FireChaos = light.FireChaos;
EnableHorror = light.EnableHorror;
MinFlickerDelay = light.MinFlickerDelay;
MaxFlickerDelay = light.MaxFlickerDelay;
DamageSeverity = light.DamageSeverity;
SparkSound = light.SparkSound;
EnableDisco = light.EnableDisco;
DiscoSpeed = light.DiscoSpeed;
DiscoSaturation = light.DiscoSaturation;
DiscoValue = light.DiscoValue;
EnableColorTransition = light.EnableColorTransition;
SecondaryColor = light.SecondaryColor;
ColorTransitionSpeed = light.ColorTransitionSpeed;
EnableSensor = light.EnableSensor;
SensorRange = light.SensorRange;
SensorMinBrightness = light.SensorMinBrightness;
SensorMaxBrightness = light.SensorMaxBrightness;
SensorSmoothness = light.SensorSmoothness;
InvertSensor = light.InvertSensor;
EnableSway = light.EnableSway;
SwaySpeedPitch = light.SwaySpeedPitch;
SwayAmountPitch = light.SwayAmountPitch;
SwaySpeedRoll = light.SwaySpeedRoll;
SwayAmountRoll = light.SwayAmountRoll;
EnablePattern = light.EnablePattern;
Pattern = light.Pattern;
PatternSpeed = light.PatternSpeed;
EnablePulse = light.EnablePulse;
PulseSpeed = light.PulseSpeed;
PulseMin = light.PulseMin;
EnableStrobe = light.EnableStrobe;
StrobeSpeed = light.StrobeSpeed;
StrobeDutyCycle = light.StrobeDutyCycle;
EnableKelvin = light.EnableKelvin;
KelvinTemperature = light.KelvinTemperature;
EnablePowerSurge = light.EnablePowerSurge;
SurgeMinInterval = light.SurgeMinInterval;
SurgeMaxInterval = light.SurgeMaxInterval;
SurgeDuration = light.SurgeDuration;
SurgeBrightnessMultiplier = light.SurgeBrightnessMultiplier;
}
public virtual void ApplyToLight( UltimateLightManager light )
{
if ( light == null )
{
return;
}
light.TargetLightType = TargetLightType;
light.SelectedPreset = SelectedPreset;
light.AutoApplyPreset = AutoApplyPreset;
light.LightGroup = LightGroup ?? "Default";
light.PowerGridTag = PowerGridTag ?? string.Empty;
light.StartDelay = StartDelay;
light.AutoDesync = AutoDesync;
light.ShowDebugGizmos = ShowDebugGizmos;
light.IsEnabled = IsEnabled;
light.LightColor = LightColor;
light.Brightness = Brightness;
light.VolumetricBoost = VolumetricBoost;
light.CastShadows = CastShadows;
light.EnableFade = EnableFade;
light.FadeInDuration = FadeInDuration;
light.FadeOutDuration = FadeOutDuration;
light.AmbientSound = AmbientSound;
light.ToggleOnSound = ToggleOnSound;
light.ToggleOffSound = ToggleOffSound;
light.ModulateVolumeWithLight = ModulateVolumeWithLight;
light.ModulatePitchWithLight = ModulatePitchWithLight;
light.BaseVolume = BaseVolume;
light.MinPitch = MinPitch;
light.MaxPitch = MaxPitch;
light.MaxDistance = MaxDistance;
light.ShadowMaxDistance = ShadowMaxDistance;
light.EnableCulling = EnableCulling;
light.EnableAdaptiveUpdates = EnableAdaptiveUpdates;
light.NearUpdateRate = NearUpdateRate;
light.FarUpdateRate = FarUpdateRate;
light.DefaultAlarmDuration = DefaultAlarmDuration;
light.AlarmColor = AlarmColor;
light.AlarmStrobeSpeed = AlarmStrobeSpeed;
light.AlarmBrightnessMultiplier = AlarmBrightnessMultiplier;
light.EnableFire = EnableFire;
light.FireSpeed = FireSpeed;
light.FireIntensity = FireIntensity;
light.FireChaos = FireChaos;
light.EnableHorror = EnableHorror;
light.MinFlickerDelay = MinFlickerDelay;
light.MaxFlickerDelay = MaxFlickerDelay;
light.DamageSeverity = DamageSeverity;
light.SparkSound = SparkSound;
light.EnableDisco = EnableDisco;
light.DiscoSpeed = DiscoSpeed;
light.DiscoSaturation = DiscoSaturation;
light.DiscoValue = DiscoValue;
light.EnableColorTransition = EnableColorTransition;
light.SecondaryColor = SecondaryColor;
light.ColorTransitionSpeed = ColorTransitionSpeed;
light.EnableSensor = EnableSensor;
light.SensorRange = SensorRange;
light.SensorMinBrightness = SensorMinBrightness;
light.SensorMaxBrightness = SensorMaxBrightness;
light.SensorSmoothness = SensorSmoothness;
light.InvertSensor = InvertSensor;
light.EnableSway = EnableSway;
light.SwaySpeedPitch = SwaySpeedPitch;
light.SwayAmountPitch = SwayAmountPitch;
light.SwaySpeedRoll = SwaySpeedRoll;
light.SwayAmountRoll = SwayAmountRoll;
light.EnablePattern = EnablePattern;
light.Pattern = Pattern ?? string.Empty;
light.PatternSpeed = PatternSpeed;
light.EnablePulse = EnablePulse;
light.PulseSpeed = PulseSpeed;
light.PulseMin = PulseMin;
light.EnableStrobe = EnableStrobe;
light.StrobeSpeed = StrobeSpeed;
light.StrobeDutyCycle = StrobeDutyCycle;
light.EnableKelvin = EnableKelvin;
light.KelvinTemperature = KelvinTemperature;
light.EnablePowerSurge = EnablePowerSurge;
light.SurgeMinInterval = SurgeMinInterval;
light.SurgeMaxInterval = SurgeMaxInterval;
light.SurgeDuration = SurgeDuration;
light.SurgeBrightnessMultiplier = SurgeBrightnessMultiplier;
}
public void NormalizeForCustomPreset()
{
SelectedPreset = UltimateLightManager.LightPreset.Custom;
AutoApplyPreset = false;
}
public UltimateLightManagerPresetData Clone()
{
var copy = new UltimateLightManagerPresetData();
copy.ApplyPresetData( this );
return copy;
}
public bool ContentEquals( UltimateLightManagerPresetData other )
{
if ( other == null )
{
return false;
}
foreach ( var property in ComparisonProperties )
{
var left = property.GetValue( this );
var right = property.GetValue( other );
if ( !AreValuesEqual( property.PropertyType, property.Name, left, right ) )
{
return false;
}
}
return true;
}
private static bool AreValuesEqual( Type propertyType, string propertyName, object left, object right )
{
if ( propertyType == typeof( float ) )
{
return Math.Abs( (float)(left ?? 0.0f) - (float)(right ?? 0.0f) ) <= 0.0001f;
}
if ( propertyType == typeof( string ) )
{
return string.Equals(
NormalizeStringValue( propertyName, left as string ),
NormalizeStringValue( propertyName, right as string ),
StringComparison.Ordinal
);
}
return Equals( left, right );
}
private static string NormalizeStringValue( string propertyName, string value )
{
value ??= string.Empty;
if ( propertyName == nameof( LightGroup ) )
{
value = string.IsNullOrWhiteSpace( value ) ? "Default" : value;
}
return value;
}
}
public sealed class UltimateLightManagerBuilderDraft : UltimateLightManagerPresetData
{
[Property, Group( "Creation" ), Order( -20 ), Title( "Light Name" )]
public string LightName { get; set; } = "Ultimate Light";
public string GetSafeLightName()
{
return string.IsNullOrWhiteSpace( LightName ) ? "Ultimate Light" : LightName.Trim();
}
public override void ApplyPresetData( UltimateLightManagerPresetData data )
{
base.ApplyPresetData( data );
}
public UltimateLightManagerPresetData ToPresetData()
{
var copy = Clone();
copy.NormalizeForCustomPreset();
return copy;
}
}