using Qwilight.Compute; using Qwilight.Utilities; using Qwilight.ViewModel; using System.Diagnostics; using System.IO; using Windows.System; using Windows.UI; namespace Qwilight { public sealed class IlluminationSystem : IDisposable { public static readonly IlluminationSystem Instance = new(); static readonly string FaultEntryPath = Path.Combine(QwilightComponent.FaultEntryPath, nameof(IlluminationSystem)); readonly BaseIlluminationSystem[] _targetSystems = new BaseIlluminationSystem[] { BWSystem.Instance, AuraSystem.Instance, K70System.Instance, LSSystem.Instance }; readonly object _availableCSX = new(); void Init() { foreach (var targetSystem in _targetSystems) { if (!targetSystem.IsHandling && targetSystem.IsAvailable) { targetSystem.IsHandling = targetSystem.Init(); } } } void OnBeforeHandle() { foreach (var targetSystem in _targetSystems) { if (targetSystem.IsHandling) { targetSystem.OnBeforeHandle(); } } } void SetInputColor(VirtualKey rawInput, uint value) { foreach (var targetSystem in _targetSystems) { if (targetSystem.IsHandling) { targetSystem.SetInputColor(rawInput, value); } } } void SetStatusColors(double status, uint value0, uint value1, uint value2, uint value3) { foreach (var targetSystem in _targetSystems) { if (targetSystem.IsHandling) { targetSystem.SetStatusColors(status, value0, value1, value2, value3); } } } void SetEtcColor(double failedValue, double meterValue) { foreach (var targetSystem in _targetSystems) { if (targetSystem.IsHandling) { if (failedValue > 0.0) { targetSystem.SetEtcColor(GetFailedColor(failedValue)); } else if (meterValue > 0.0) { targetSystem.SetEtcColor(GetValueColor(targetSystem.GetMeterColor(), meterValue)); } } } } void OnHandled() { foreach (var targetSystem in _targetSystems) { if (targetSystem.IsHandling) { targetSystem.OnHandled(); } } } public object HandlingCSX { get; } = new(); public bool[] HasInputValues { get; } = new bool[53]; public double[] InputValues { get; } = new double[53]; public bool IsMeter { get; set; } public bool IsFailed { get; set; } bool IsAvailable => _targetSystems.Any(targetSystem => targetSystem.IsAvailable); public void HandleIfAvailable() { lock (_availableCSX) { if (IsAvailable) { Monitor.Pulse(_availableCSX); } } } public void HandleSystem() { var voteViewModel = ViewModels.Instance.VoteValue; var wwwLevelViewModel = ViewModels.Instance.WwwLevelValue; var assistViewModel = ViewModels.Instance.AssistValue; var mainViewModel = ViewModels.Instance.MainValue; var inputViewModel = ViewModels.Instance.InputValue; var toNotifyViewModel = ViewModels.Instance.NotifyValue; var inputStandardViewModel = ViewModels.Instance.InputStandardValue; var siteContainerViewModel = ViewModels.Instance.SiteContainerValue; var valueConfigureViewModel = ViewModels.Instance.ConfigureValue; var defaultStatusInputs = new[] { VirtualKey.Number1, VirtualKey.Number2, VirtualKey.Number3, VirtualKey.Number4, VirtualKey.Number5, VirtualKey.Number6, VirtualKey.Number7, VirtualKey.Number8, VirtualKey.Number9, VirtualKey.Number0, }; var defaultStatusInputsLength = defaultStatusInputs.Length; var defaultStatusInputsUnit = 1.0 / defaultStatusInputsLength; var mainHitPointsInputs = new[] { VirtualKey.Number1, VirtualKey.Number2, VirtualKey.Number3, VirtualKey.Number4, VirtualKey.Number5, }; var mainHitPointsInputsLength = mainHitPointsInputs.Length; var mainHitPointsInputsUnit = 1.0 / mainHitPointsInputsLength; var mainStatusInputs = new[] { VirtualKey.Number6, VirtualKey.Number7, VirtualKey.Number8, VirtualKey.Number9, VirtualKey.Number0, }; var mainStatusInputsLength = mainStatusInputs.Length; var mainStatusInputsUnit = 1.0 / mainStatusInputsLength; var audioVisualizerInputs = new VirtualKey[10][] { new[] { VirtualKey.Z, VirtualKey.A, VirtualKey.Q, }, new[] { VirtualKey.X, VirtualKey.S, VirtualKey.W, }, new[] { VirtualKey.C, VirtualKey.D, VirtualKey.E, }, new[] { VirtualKey.V, VirtualKey.F, VirtualKey.R, }, new[] { VirtualKey.B, VirtualKey.G, VirtualKey.T, }, new[] { VirtualKey.N, VirtualKey.H, VirtualKey.Y, }, new[] { VirtualKey.M, VirtualKey.J, VirtualKey.U, }, new[] { (VirtualKey)188, VirtualKey.K, VirtualKey.I, }, new[] { (VirtualKey)190, VirtualKey.L, VirtualKey.O, }, new[] { (VirtualKey)191, (VirtualKey)186, VirtualKey.P, } }; var audioVisualizerInputsLength = audioVisualizerInputs.Length; var audioMainVisualizerValues = new double[audioVisualizerInputsLength]; var audioMainVisualizerFrames = new double[audioVisualizerInputsLength]; var lastAudioVisualizerMainValues = new double[audioVisualizerInputsLength]; var audioInputVisualizerValues = new double[audioVisualizerInputsLength]; var audioInputVisualizerFrames = new double[audioVisualizerInputsLength]; var lastAudioVisualizerInputValues = new double[audioVisualizerInputsLength]; var audioVisualizerColors = new Color[audioVisualizerInputs.Length, 3, 2]; var meterValue = 0.0; var failedValue = 0.0; var handlingColor = GetHandlingColor(1.0); var inputStandardColor = GetInputStandardColor(1.0); var defaultColor = GetDefaultColor(1.0); var inputColor = GetInputColor(0.0); var loopingCounter = 0.0; var loopingHandler = Stopwatch.StartNew(); while (true) { try { lock (HandlingCSX) { Init(); OnBeforeHandle(); SetInputColor(VirtualKey.F6, defaultColor); SetInputColor(VirtualKey.F7, voteViewModel.IsOpened ? handlingColor : defaultColor); SetInputColor(VirtualKey.F8, siteContainerViewModel.IsOpened ? handlingColor : defaultColor); SetInputColor(VirtualKey.F10, wwwLevelViewModel.IsOpened ? handlingColor : defaultColor); SetInputColor(VirtualKey.F11, toNotifyViewModel.IsOpened ? handlingColor : defaultColor); if (!QwilightComponent.IsValve) { SetInputColor(VirtualKey.F12, defaultColor); } var isInputStandardWindowOpened = inputStandardViewModel.IsOpened; var isDefaultInputWindowOpened = inputViewModel.IsOpened && inputViewModel.ControllerModeValue == InputViewModel.ControllerMode.DefaultInput; var defaultComputer = mainViewModel.Computer; switch (mainViewModel.ModeValue) { case MainViewModel.Mode.NoteFile: PaintInputAudioVisualizer(); PaintEtc(); var autoComputer = mainViewModel.AutoComputer; if (autoComputer != null) { PaintInputStatus(autoComputer); PaintStatusStatus(autoComputer); } SetInputColor(VirtualKey.Escape, defaultColor); SetInputColor(VirtualKey.Enter, defaultColor); SetInputColor(VirtualKey.Delete, defaultColor); SetInputColor(VirtualKey.Left, defaultColor); SetInputColor(VirtualKey.Right, defaultColor); SetInputColor(VirtualKey.Up, defaultColor); SetInputColor(VirtualKey.Down, defaultColor); SetInputColor(VirtualKey.Space, defaultColor); SetInputColor(VirtualKey.F1, assistViewModel.IsOpened ? handlingColor : defaultColor); SetInputColor(VirtualKey.F5, mainViewModel.IsDefaultEntryLoading ? handlingColor : defaultColor); break; case MainViewModel.Mode.Computing: PaintEtc(); var handlingHitPointsColor = defaultComputer.ModeComponentValue.HandlingHitPointsColor; var hitPoints = defaultComputer.HitPoints.Value; for (var i = mainHitPointsInputsLength - 1; i >= 0; --i) { var value = Math.Clamp((hitPoints - ((double)i / mainHitPointsInputsLength)) / mainHitPointsInputsUnit, 0, 1); SetInputColor(mainHitPointsInputs[i], GetValueColor(handlingHitPointsColor, value)); } SetStatusColors(hitPoints, handlingHitPointsColor.R, handlingHitPointsColor.G, handlingHitPointsColor.B, handlingHitPointsColor.A); var status = defaultComputer.Status; for (var i = mainStatusInputsLength - 1; i >= 0; --i) { var value = Math.Clamp((status - (double)i / mainStatusInputsLength) / mainStatusInputsUnit, 0, 1); SetInputColor(mainStatusInputs[i], GetStatusColor(value * meterValue)); } SetInputColor(VirtualKey.Escape, defaultComputer.IsPausingWindowOpened ? handlingColor : defaultColor); SetInputColor(VirtualKey.Enter, defaultComputer.IsPassable || defaultComputer.IsPausingWindowOpened ? defaultColor : handlingColor); if (defaultComputer.IsPausingWindowOpened) { SetInputColor(VirtualKey.Up, defaultColor); SetInputColor(VirtualKey.Down, defaultColor); SetInputColor(VirtualKey.Space, defaultColor); } if (!isInputStandardWindowOpened) { for (var i = Configure.Instance.DefaultInputBundlesV6.StandardInputs.Length - 1; i >= 0; --i) { switch (i) { case InputStandardViewModel.ModifyAutoMode: if (defaultComputer.CanModifyAutoMode) { SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[i].Data, defaultComputer.IsAutoMode ? inputStandardColor : handlingColor); } break; case InputStandardViewModel.Media: SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[i].Data, Configure.Instance.LoadedMedia && Configure.Instance.Media ? inputStandardColor : handlingColor); break; case InputStandardViewModel.PostItem0: case InputStandardViewModel.PostItem1: if (defaultComputer.PostableItems[0] != null || defaultComputer.PostableItems[1] != null) { SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[i].Data, inputStandardColor); } break; default: SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[i].Data, inputStandardColor); break; } } } if (!isDefaultInputWindowOpened) { var inputs = Configure.Instance.DefaultInputBundlesV6.Inputs[(int)defaultComputer.InputMode]; if (inputs != null) { for (var i = inputs.Length - 1; i > 0; --i) { foreach (var defaultInput in inputs[i]) { SetInputColor(defaultInput.Data, failedValue > 0.0 ? GetFailedColor(failedValue) : GetInputColor(InputValues[i])); } } } } for (var i = InputValues.Length - 1; i > 0; --i) { if (!HasInputValues[i]) { InputValues[i] = Math.Max(0.0, InputValues[i] - (60 / 1000.0)); } } break; case MainViewModel.Mode.Quit: PaintInputAudioVisualizer(); PaintEtc(); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.Media].Data, Configure.Instance.Media ? inputStandardColor : handlingColor); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.Undo].Data, inputStandardColor); SetInputColor(VirtualKey.Escape, defaultColor); SetInputColor(VirtualKey.Enter, defaultColor); if (0 < defaultComputer.LevyingComputingPosition) { SetInputColor(VirtualKey.Left, defaultColor); } if (defaultComputer.LevyingComputingPosition < defaultComputer.HighestComputingPosition) { SetInputColor(VirtualKey.Right, defaultColor); } break; } if (isInputStandardWindowOpened) { for (var i = Configure.Instance.DefaultInputBundlesV6.StandardInputs.Length - 1; i >= 0; --i) { SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[i].Data, inputStandardColor); } } else { SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.LowerMultiplier].Data, inputStandardColor); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.HigherMultiplier].Data, inputStandardColor); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.LowerAudioMultiplier].Data, inputStandardColor); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.HigherAudioMultiplier].Data, inputStandardColor); SetInputColor(Configure.Instance.DefaultInputBundlesV6.StandardInputs[InputStandardViewModel.Media].Data, Configure.Instance.Media ? inputStandardColor : handlingColor); } if (isDefaultInputWindowOpened) { var inputControllerInputs = Configure.Instance.DefaultInputBundlesV6.Inputs[(int)inputViewModel.InputMode]; if (inputControllerInputs != null) { foreach (var inputControllerInput in inputControllerInputs) { if (inputControllerInput != null) { foreach (var defaultInput in inputControllerInput) { SetInputColor(defaultInput.Data, inputColor); } } } } } var modeComponentValue = mainViewModel.ModeComponentValue; if (valueConfigureViewModel.IsOpened && valueConfigureViewModel.TabPosition == 0 && valueConfigureViewModel.TabPositionComputing == 0 && (mainViewModel.CanModifyModeComponent || modeComponentValue.CanModifyMultiplier || modeComponentValue.CanModifyAudioMultiplier)) { SetInputColor(VirtualKey.F1, defaultColor); SetInputColor(VirtualKey.F2, defaultColor); SetInputColor(VirtualKey.F3, defaultColor); SetInputColor(VirtualKey.F4, defaultColor); SetInputColor(VirtualKey.F5, defaultColor); SetInputColor(VirtualKey.F6, defaultColor); SetInputColor(VirtualKey.F7, defaultColor); SetInputColor(VirtualKey.F8, defaultColor); } if (IsMeter) { meterValue = 1.0; IsMeter = false; } else { meterValue = Math.Max(0.0, meterValue - 60 / 1000.0); } if (IsFailed) { failedValue = 1.0; IsFailed = false; } else { failedValue = Math.Max(0.0, failedValue - 60 / QwilightComponent.StandardWaitMillis); } OnHandled(); } loopingCounter += 1000.0 / 60; var toWait = loopingCounter - loopingHandler.GetMillis(); if (toWait > 0.0) { Thread.Sleep(TimeSpan.FromMilliseconds(toWait)); } lock (_availableCSX) { if (!IsAvailable) { Monitor.Wait(_availableCSX); } } void PaintEtc() { SetEtcColor(failedValue, meterValue); } void PaintStatusStatus(DefaultCompute defaultComputer) { var statusColor = defaultComputer.IsHandling ? defaultComputer.IsPausing ? BaseUI.Instance.StatusPausedColor : BaseUI.Instance.StatusHandlingColor : BaseUI.Instance.StatusLoadingNoteFileColor; var handlingMeterValue = defaultComputer.IsHandling && !defaultComputer.IsPausing ? meterValue : 1.0; SetStatusColors(defaultComputer.Status, (uint)(statusColor.R * handlingMeterValue), (uint)(statusColor.G * handlingMeterValue), (uint)(statusColor.B * handlingMeterValue), (uint)(statusColor.A * handlingMeterValue)); } void PaintInputStatus(DefaultCompute defaultComputer) { var statusColor = defaultComputer.IsHandling ? defaultComputer.IsPausing ? BaseUI.Instance.StatusPausedColor : BaseUI.Instance.StatusHandlingColor : BaseUI.Instance.StatusLoadingNoteFileColor; var handlingMeterValue = defaultComputer.IsHandling && !defaultComputer.IsPausing ? meterValue : 1.0; for (var i = defaultStatusInputsLength - 1; i >= 0; --i) { SetInputColor(defaultStatusInputs[i], GetValueColor(statusColor, handlingMeterValue * Math.Clamp((defaultComputer.Status - (double)i / defaultStatusInputsLength) / defaultStatusInputsUnit, 0, 1))); } } void PaintInputAudioVisualizer() { for (var i = audioVisualizerInputsLength - 1; i >= 0; --i) { var audioMainVisualizerValue = 0.0; var audioInputVisualizerValue = 0.0; for (var j = ((i + 1) * Configure.Instance.AudioVisualizerCount / audioVisualizerInputsLength) - 1; j >= i * Configure.Instance.AudioVisualizerCount / audioVisualizerInputsLength; --j) { audioMainVisualizerValue = Math.Max(audioMainVisualizerValue, AudioSystem.Instance.GetAudioVisualizerValue(AudioSystem.MainAudio, j)); audioInputVisualizerValue = Math.Max(audioInputVisualizerValue, AudioSystem.Instance.GetAudioVisualizerValue(AudioSystem.InputAudio, j)); } audioMainVisualizerValues[i] = audioMainVisualizerValue; audioInputVisualizerValues[i] = audioInputVisualizerValue; var distance = lastAudioVisualizerMainValues[i] - audioMainVisualizerValues[i]; audioMainVisualizerFrames[i] = Math.Clamp(audioMainVisualizerFrames[i] + (distance == 0.0 ? 0.0 : distance > 0 ? -1 / 3.0 : 1 / 3.0), 0.0, 3.0); distance = lastAudioVisualizerInputValues[i] - audioInputVisualizerValues[i]; audioInputVisualizerFrames[i] = Math.Clamp(audioInputVisualizerFrames[i] + (distance == 0.0 ? 0.0 : distance > 0 ? -1 / 3.0 : 1 / 3.0), 0.0, 3.0); } Array.Copy(audioMainVisualizerValues, lastAudioVisualizerMainValues, audioVisualizerInputsLength); Array.Copy(audioInputVisualizerValues, lastAudioVisualizerInputValues, audioVisualizerInputsLength); Array.Clear(audioVisualizerColors, 0, audioVisualizerColors.Length); var audioVisualizerMainColor = Configure.Instance.AudioVisualizerMainColor; var audioVisualizerInputColor = Configure.Instance.AudioVisualizerInputColor; for (var i = audioVisualizerInputsLength - 1; i >= 0; --i) { var value = audioMainVisualizerFrames[i]; var valueInt = (int)value; var remainder = value - valueInt; if (remainder > 0.0) { audioVisualizerColors[i, valueInt, 0] = new() { R = (byte)(audioVisualizerMainColor.R * remainder), G = (byte)(audioVisualizerMainColor.R * remainder), B = (byte)(audioVisualizerMainColor.R * remainder), }; } for (var j = valueInt - 1; j >= 0; --j) { audioVisualizerColors[i, j, 0] = audioVisualizerMainColor; } value = audioInputVisualizerFrames[i]; valueInt = (int)value; remainder = value - valueInt; if (remainder > 0.0) { audioVisualizerColors[i, valueInt, 1] = new() { R = (byte)(audioVisualizerInputColor.R * remainder), G = (byte)(audioVisualizerInputColor.R * remainder), B = (byte)(audioVisualizerInputColor.R * remainder), }; } for (var j = valueInt - 1; j >= 0; --j) { audioVisualizerColors[i, j, 1] = audioVisualizerInputColor; } for (var j = audioVisualizerColors.GetLength(0) - 1; j >= 0; --j) { for (var m = audioVisualizerColors.GetLength(1) - 1; m >= 0; --m) { SetInputColor(audioVisualizerInputs[j][m], GetValueColor(new() { A = (byte)((audioVisualizerColors[j, m, 0].A + audioVisualizerColors[j, m, 1].A) / 2), R = (byte)((audioVisualizerColors[j, m, 0].R + audioVisualizerColors[j, m, 1].R) / 2), G = (byte)((audioVisualizerColors[j, m, 0].G + audioVisualizerColors[j, m, 1].G) / 2), B = (byte)((audioVisualizerColors[j, m, 0].B + audioVisualizerColors[j, m, 1].B) / 2) }, 1.0)); } } } } } catch (ThreadInterruptedException) { } catch (Exception e) { Utility.SaveFaultFile(FaultEntryPath, e); } } } uint GetDefaultColor(double status) => Utility.GetColor((uint)(0 * status), (uint)(127 * status), (uint)(255 * status), 255); uint GetHandlingColor(double status) => Utility.GetColor((uint)(255 * status), (uint)(127 * status), (uint)(0 * status), 255); uint GetInputColor(double status) => Utility.GetColor((uint)(0 * status), (uint)(255 * (1.0 - status)), (uint)(255 * status), 255); uint GetInputStandardColor(double status) => Utility.GetColor((uint)(255 * status), (uint)(255 * status), (uint)(0 * status), 255); uint GetFailedColor(double status) => Utility.GetColor((uint)(255 * status), (uint)(0 * status), (uint)(0 * status), 255); uint GetStatusColor(double status) => Utility.GetColor((uint)(0 * status), (uint)(255 * status), (uint)(255 * status), 255); uint GetValueColor(Color value, double status) => Utility.GetColor((uint)(value.R * status), (uint)(value.G * status), (uint)(value.B * status), 255); public void Dispose() { foreach (var targetSystem in _targetSystems) { targetSystem.Dispose(); } } } }