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 | ||||
| 
 | ||||
| - Fixed wrong colors during fade out transition, e.g. in Mineshaft tunnel tiles. | ||||
| 
 | ||||
| ## MuzikaGromche 1337.420.9002 - Anime Edition | ||||
| 
 | ||||
|  |  | |||
|  | @ -597,22 +597,6 @@ namespace MuzikaGromche | |||
|             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 | ||||
|         public const float AudioMaxDistance = 150; | ||||
| 
 | ||||
|  | @ -1559,9 +1543,7 @@ namespace MuzikaGromche | |||
|             if (windUpOffsetTimestamp.Beat < 0f && track.FadeOutBeat < loopOffsetSpan.BeatToInclusive && loopOffsetSpan.BeatFromExclusive <= fadeOutEnd) | ||||
|             { | ||||
|                 var t = (loopOffsetSpan.BeatToInclusive - track.FadeOutBeat) / track.FadeOutDuration; | ||||
|                 // TODO: assumes that default lights color is white | ||||
|                 var DefaultLightsColor = Color.white; | ||||
|                 return new SetLightsColorTransitionEvent(DefaultLightsColor, Color.black, Easing.Linear, t); | ||||
|                 return new SetLightsColorTransitionEvent(/* use initial light color */null, Color.black, Easing.Linear, t); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|  | @ -1610,9 +1592,9 @@ namespace MuzikaGromche | |||
|                 } | ||||
|             } | ||||
|             // 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 transitionEnd = clipsBoundary + track.ColorTransitionOut; | ||||
|  | @ -1625,7 +1607,7 @@ namespace MuzikaGromche | |||
|                 return new SetLightsColorTransitionEvent(ColorAtWholeBeat(transitionStart), ColorAtWholeBeat(transitionEnd), track.ColorTransitionEasing, t); | ||||
|             } | ||||
| 
 | ||||
|             Color ColorAtWholeBeat(BeatTimestamp timestamp) | ||||
|             Color? ColorAtWholeBeat(BeatTimestamp timestamp) | ||||
|             { | ||||
|                 if (timestamp.Beat >= 0f) | ||||
|                 { | ||||
|  | @ -1634,9 +1616,7 @@ namespace MuzikaGromche | |||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // TODO: assumes that default lights color is white | ||||
|                     var DefaultLightsColor = Color.white; | ||||
|                     return float.IsNaN(track.FadeOutBeat) ? DefaultLightsColor : Color.black; | ||||
|                     return float.IsNaN(track.FadeOutBeat) ? /* use initial light color */ null : Color.black; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -1644,26 +1624,55 @@ namespace MuzikaGromche | |||
| 
 | ||||
|     abstract class BaseEvent; | ||||
| 
 | ||||
|     class SetLightsColorEvent(Color color) : BaseEvent | ||||
|     abstract class SetLightsColorEvent : BaseEvent | ||||
|     { | ||||
|         public readonly Color Color = color; | ||||
|         public override string ToString() | ||||
|         // Calculate final color, substituting null with initialColor if needed. | ||||
|         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) | ||||
|         : SetLightsColorEvent(Color.Lerp(from, to, Mathf.Clamp(easing.Eval(t), 0f, 1f))) | ||||
|     class SetLightsColorStaticEvent(Color? color) : 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 readonly Color? Color = color; | ||||
| 
 | ||||
|         public override Color GetColor(Color initialColor) | ||||
|         { | ||||
|             return Color ?? initialColor; | ||||
|         } | ||||
| 
 | ||||
|         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 | ||||
|             if (__state.previousState == 2 && __instance.previousState != 2) | ||||
|             { | ||||
|                 Plugin.ResetLightColor(); | ||||
|                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||
|                 DiscoBallManager.Disable(); | ||||
|                 // Rotate track groups | ||||
|                 behaviour.ChooseTrackServerRpc(); | ||||
|  | @ -2458,7 +2467,7 @@ namespace MuzikaGromche | |||
|                             break; | ||||
| 
 | ||||
|                         case SetLightsColorEvent e: | ||||
|                             Plugin.SetLightColor(e.Color); | ||||
|                             PoweredLightsBehaviour.Instance.SetLightColor(e); | ||||
|                             break; | ||||
| 
 | ||||
|                         case FlickerLightsEvent: | ||||
|  | @ -2488,7 +2497,7 @@ namespace MuzikaGromche | |||
|         { | ||||
|             if (__instance is JesterAI) | ||||
|             { | ||||
|                 Plugin.ResetLightColor(); | ||||
|                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||
|                 DiscoBallManager.Disable(); | ||||
|                 // Just in case if players have spawned multiple Jesters, | ||||
|                 // Don't reset Plugin.CurrentTrack to null, | ||||
|  |  | |||
|  | @ -252,7 +252,10 @@ namespace MuzikaGromche | |||
|         static bool OnRefreshLightsList(RoundManager __instance) | ||||
|         { | ||||
|             RefreshLightsListPatched(__instance); | ||||
|             LoadInitialLightsColors(__instance); | ||||
| 
 | ||||
|             var behaviour = __instance.gameObject.GetComponent<PoweredLightsBehaviour>() ?? __instance.gameObject.AddComponent<PoweredLightsBehaviour>(); | ||||
|             behaviour.Refresh(); | ||||
| 
 | ||||
|             // Skip the original method | ||||
|             return false; | ||||
|         } | ||||
|  | @ -287,15 +290,56 @@ namespace MuzikaGromche | |||
|                 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