Substitute placeholder nulls with per-light initial color for transitions
Fade out and first color transitions used to assume white as a default color, which is not always the case e.g. in Mineshaft tunnel tiles. Use nullable from/to/color fields, and substitute them with per-light initial color data in a new virtual method that calculates colors.
This commit is contained in:
		
							parent
							
								
									bbd9b0204f
								
							
						
					
					
						commit
						99babe8bdf
					
				|  | @ -2,6 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| ## MuzikaGromche 1337.420.9003 | ## MuzikaGromche 1337.420.9003 | ||||||
| 
 | 
 | ||||||
|  | - Fixed wrong colors during fade out transition, e.g. in Mineshaft tunnel tiles. | ||||||
| 
 | 
 | ||||||
| ## MuzikaGromche 1337.420.9002 - Anime Edition | ## MuzikaGromche 1337.420.9002 - Anime Edition | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -597,22 +597,6 @@ namespace MuzikaGromche | ||||||
|             return Tracks.SelectMany(track => track.GetTracks()).FirstOrDefault(track => track.Name == name); |             return Tracks.SelectMany(track => track.GetTracks()).FirstOrDefault(track => track.Name == name); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public static void SetLightColor(Color color) |  | ||||||
|         { |  | ||||||
|             foreach (var light in RoundManager.Instance.allPoweredLights) |  | ||||||
|             { |  | ||||||
|                 light.color = color; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void ResetLightColor() |  | ||||||
|         { |  | ||||||
|             foreach (var (light, color) in InitialLightsColors) |  | ||||||
|             { |  | ||||||
|                 light.color = color; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Max audible distance for AudioSource and LyricsEvent |         // Max audible distance for AudioSource and LyricsEvent | ||||||
|         public const float AudioMaxDistance = 150; |         public const float AudioMaxDistance = 150; | ||||||
| 
 | 
 | ||||||
|  | @ -1559,9 +1543,7 @@ namespace MuzikaGromche | ||||||
|             if (windUpOffsetTimestamp.Beat < 0f && track.FadeOutBeat < loopOffsetSpan.BeatToInclusive && loopOffsetSpan.BeatFromExclusive <= fadeOutEnd) |             if (windUpOffsetTimestamp.Beat < 0f && track.FadeOutBeat < loopOffsetSpan.BeatToInclusive && loopOffsetSpan.BeatFromExclusive <= fadeOutEnd) | ||||||
|             { |             { | ||||||
|                 var t = (loopOffsetSpan.BeatToInclusive - track.FadeOutBeat) / track.FadeOutDuration; |                 var t = (loopOffsetSpan.BeatToInclusive - track.FadeOutBeat) / track.FadeOutDuration; | ||||||
|                 // TODO: assumes that default lights color is white |                 return new SetLightsColorTransitionEvent(/* use initial light color */null, Color.black, Easing.Linear, t); | ||||||
|                 var DefaultLightsColor = Color.white; |  | ||||||
|                 return new SetLightsColorTransitionEvent(DefaultLightsColor, Color.black, Easing.Linear, t); |  | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|  | @ -1610,9 +1592,9 @@ namespace MuzikaGromche | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             // default |             // default | ||||||
|             return new SetLightsColorEvent(ColorAtWholeBeat(timestamp)); |             return new SetLightsColorStaticEvent(ColorAtWholeBeat(timestamp)); | ||||||
| 
 | 
 | ||||||
|             SetLightsColorEvent ColorTransition(BeatTimestamp clipsBoundary) |             SetLightsColorTransitionEvent ColorTransition(BeatTimestamp clipsBoundary) | ||||||
|             { |             { | ||||||
|                 var transitionStart = clipsBoundary - track.ColorTransitionIn; |                 var transitionStart = clipsBoundary - track.ColorTransitionIn; | ||||||
|                 var transitionEnd = clipsBoundary + track.ColorTransitionOut; |                 var transitionEnd = clipsBoundary + track.ColorTransitionOut; | ||||||
|  | @ -1625,7 +1607,7 @@ namespace MuzikaGromche | ||||||
|                 return new SetLightsColorTransitionEvent(ColorAtWholeBeat(transitionStart), ColorAtWholeBeat(transitionEnd), track.ColorTransitionEasing, t); |                 return new SetLightsColorTransitionEvent(ColorAtWholeBeat(transitionStart), ColorAtWholeBeat(transitionEnd), track.ColorTransitionEasing, t); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             Color ColorAtWholeBeat(BeatTimestamp timestamp) |             Color? ColorAtWholeBeat(BeatTimestamp timestamp) | ||||||
|             { |             { | ||||||
|                 if (timestamp.Beat >= 0f) |                 if (timestamp.Beat >= 0f) | ||||||
|                 { |                 { | ||||||
|  | @ -1634,9 +1616,7 @@ namespace MuzikaGromche | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     // TODO: assumes that default lights color is white |                     return float.IsNaN(track.FadeOutBeat) ? /* use initial light color */ null : Color.black; | ||||||
|                     var DefaultLightsColor = Color.white; |  | ||||||
|                     return float.IsNaN(track.FadeOutBeat) ? DefaultLightsColor : Color.black; |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -1644,26 +1624,55 @@ namespace MuzikaGromche | ||||||
| 
 | 
 | ||||||
|     abstract class BaseEvent; |     abstract class BaseEvent; | ||||||
| 
 | 
 | ||||||
|     class SetLightsColorEvent(Color color) : BaseEvent |     abstract class SetLightsColorEvent : BaseEvent | ||||||
|     { |     { | ||||||
|         public readonly Color Color = color; |         // Calculate final color, substituting null with initialColor if needed. | ||||||
|         public override string ToString() |         public abstract Color GetColor(Color initialColor); | ||||||
|  | 
 | ||||||
|  |         protected string NullableColorToString(Color? color) | ||||||
|         { |         { | ||||||
|             return $"Color(#{ColorUtility.ToHtmlStringRGB(Color)})"; |             return color is { } c ? ColorUtility.ToHtmlStringRGB(c) : "??????"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     class SetLightsColorTransitionEvent(Color from, Color to, Easing easing, float t) |     class SetLightsColorStaticEvent(Color? color) : SetLightsColorEvent | ||||||
|         : SetLightsColorEvent(Color.Lerp(from, to, Mathf.Clamp(easing.Eval(t), 0f, 1f))) |  | ||||||
|     { |     { | ||||||
|         // Additional context for debugging |         public readonly Color? Color = color; | ||||||
|         public readonly Color From = from; | 
 | ||||||
|         public readonly Color To = to; |         public override Color GetColor(Color initialColor) | ||||||
|         public readonly Easing Easing = easing; |         { | ||||||
|         public readonly float T = t; |             return Color ?? initialColor; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public override string ToString() |         public override string ToString() | ||||||
|         { |         { | ||||||
|             return $"Color(#{ColorUtility.ToHtmlStringRGB(Color)} = #{ColorUtility.ToHtmlStringRGB(From)}..#{ColorUtility.ToHtmlStringRGB(To)} {Easing} {T:N4})"; |             return $"Color(#{NullableColorToString(Color)})"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     class SetLightsColorTransitionEvent(Color? from, Color? to, Easing easing, float t) : SetLightsColorEvent | ||||||
|  |     { | ||||||
|  |         // Additional context for debugging | ||||||
|  |         public readonly Color? From = from; | ||||||
|  |         public readonly Color? To = to; | ||||||
|  |         public readonly Easing Easing = easing; | ||||||
|  |         public readonly float T = t; | ||||||
|  | 
 | ||||||
|  |         public override Color GetColor(Color initialColor) | ||||||
|  |         { | ||||||
|  |             var from = From ?? initialColor; | ||||||
|  |             var to = To ?? initialColor; | ||||||
|  |             return Color.Lerp(from, to, Mathf.Clamp(Easing.Eval(T), 0f, 1f)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Color? GetNullableColor() | ||||||
|  |         { | ||||||
|  |             return From is { } from && To is { } to ? Color.Lerp(from, to, Mathf.Clamp(Easing.Eval(T), 0f, 1f)) : null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override string ToString() | ||||||
|  |         { | ||||||
|  |             return $"Color(#{NullableColorToString(GetNullableColor())} = #{NullableColorToString(From)}..#{NullableColorToString(To)} {Easing} {T:N4})"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -2438,7 +2447,7 @@ namespace MuzikaGromche | ||||||
|             // transition away from state 2 ("poppedOut"), normally to state 0 |             // transition away from state 2 ("poppedOut"), normally to state 0 | ||||||
|             if (__state.previousState == 2 && __instance.previousState != 2) |             if (__state.previousState == 2 && __instance.previousState != 2) | ||||||
|             { |             { | ||||||
|                 Plugin.ResetLightColor(); |                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||||
|                 DiscoBallManager.Disable(); |                 DiscoBallManager.Disable(); | ||||||
|                 // Rotate track groups |                 // Rotate track groups | ||||||
|                 behaviour.ChooseTrackServerRpc(); |                 behaviour.ChooseTrackServerRpc(); | ||||||
|  | @ -2458,7 +2467,7 @@ namespace MuzikaGromche | ||||||
|                             break; |                             break; | ||||||
| 
 | 
 | ||||||
|                         case SetLightsColorEvent e: |                         case SetLightsColorEvent e: | ||||||
|                             Plugin.SetLightColor(e.Color); |                             PoweredLightsBehaviour.Instance.SetLightColor(e); | ||||||
|                             break; |                             break; | ||||||
| 
 | 
 | ||||||
|                         case FlickerLightsEvent: |                         case FlickerLightsEvent: | ||||||
|  | @ -2488,7 +2497,7 @@ namespace MuzikaGromche | ||||||
|         { |         { | ||||||
|             if (__instance is JesterAI) |             if (__instance is JesterAI) | ||||||
|             { |             { | ||||||
|                 Plugin.ResetLightColor(); |                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||||
|                 DiscoBallManager.Disable(); |                 DiscoBallManager.Disable(); | ||||||
|                 // Just in case if players have spawned multiple Jesters, |                 // Just in case if players have spawned multiple Jesters, | ||||||
|                 // Don't reset Plugin.CurrentTrack to null, |                 // Don't reset Plugin.CurrentTrack to null, | ||||||
|  |  | ||||||
|  | @ -252,7 +252,10 @@ namespace MuzikaGromche | ||||||
|         static bool OnRefreshLightsList(RoundManager __instance) |         static bool OnRefreshLightsList(RoundManager __instance) | ||||||
|         { |         { | ||||||
|             RefreshLightsListPatched(__instance); |             RefreshLightsListPatched(__instance); | ||||||
|             LoadInitialLightsColors(__instance); | 
 | ||||||
|  |             var behaviour = __instance.gameObject.GetComponent<PoweredLightsBehaviour>() ?? __instance.gameObject.AddComponent<PoweredLightsBehaviour>(); | ||||||
|  |             behaviour.Refresh(); | ||||||
|  | 
 | ||||||
|             // Skip the original method |             // Skip the original method | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  | @ -287,15 +290,56 @@ namespace MuzikaGromche | ||||||
|                 animator.SetFloat("flickerSpeed", UnityEngine.Random.Range(0.6f, 1.4f)); |                 animator.SetFloat("flickerSpeed", UnityEngine.Random.Range(0.6f, 1.4f)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         static void LoadInitialLightsColors(RoundManager self) |  | ||||||
|         { |  | ||||||
|             var originalColors = new Dictionary<Light, Color>(); |  | ||||||
|             foreach (var light in self.allPoweredLights) |  | ||||||
|             { |  | ||||||
|                 originalColors[light] = light.color; |  | ||||||
|     } |     } | ||||||
|             Plugin.InitialLightsColors = originalColors; | 
 | ||||||
|  |     internal class PoweredLightsBehaviour : MonoBehaviour | ||||||
|  |     { | ||||||
|  |         private struct LightData | ||||||
|  |         { | ||||||
|  |             public Light Light; | ||||||
|  |             public Color InitialColor; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly List<LightData> AllPoweredLights = []; | ||||||
|  | 
 | ||||||
|  |         public static PoweredLightsBehaviour Instance { get; private set; } = null!; | ||||||
|  | 
 | ||||||
|  |         void Awake() | ||||||
|  |         { | ||||||
|  |             if (Instance == null) | ||||||
|  |             { | ||||||
|  |                 Instance = this; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Refresh() | ||||||
|  |         { | ||||||
|  |             AllPoweredLights.Clear(); | ||||||
|  |             foreach (var light in RoundManager.Instance.allPoweredLights) | ||||||
|  |             { | ||||||
|  |                 AllPoweredLights.Add(new LightData | ||||||
|  |                 { | ||||||
|  |                     Light = light, | ||||||
|  |                     InitialColor = light.color, | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SetLightColor(SetLightsColorEvent e) | ||||||
|  |         { | ||||||
|  |             foreach (var data in AllPoweredLights) | ||||||
|  |             { | ||||||
|  |                 var color = e.GetColor(data.InitialColor); | ||||||
|  |                 data.Light.color = color; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ResetLightColor() | ||||||
|  |         { | ||||||
|  |             foreach (var data in AllPoweredLights) | ||||||
|  |             { | ||||||
|  |                 data.Light.color = data.InitialColor; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue