forked from nikita/muzika-gromche
				
			Compare commits
	
		
			No commits in common. "ceaac4e01b870f2897c6abd9e165cc36c6135994" and "585ef604ffcdbcb23a39db6f57e3ca5dfb34bc87" have entirely different histories.
		
	
	
		
			ceaac4e01b
			...
			585ef604ff
		
	
		
							
								
								
									
										
											BIN
										
									
								
								Assets/AttentionPls1Intro.ogg (Stored with Git LFS)
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								Assets/AttentionPls1Intro.ogg (Stored with Git LFS)
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Assets/AttentionPls2Intro.ogg (Stored with Git LFS)
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								Assets/AttentionPls2Intro.ogg (Stored with Git LFS)
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Assets/AttentionPlsLoop.ogg (Stored with Git LFS)
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								Assets/AttentionPlsLoop.ogg (Stored with Git LFS)
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -1,10 +1,5 @@ | |||
| # Changelog | ||||
| 
 | ||||
| ## MuzikaGromche 1337.420.9004 - Life Support Edition | ||||
| 
 | ||||
| - Override Death Screen / Game Over text in certain cases. | ||||
| - Added a new track AttentionPls featuring multiple intro variants and new visual effects. | ||||
| 
 | ||||
| ## MuzikaGromche 1337.420.9003 - Lights Out Edition | ||||
| 
 | ||||
| - Fixed wrong colors during fade out transition, e.g. in Mineshaft tunnel tiles. | ||||
|  |  | |||
|  | @ -1,72 +0,0 @@ | |||
| using System.Collections; | ||||
| using HarmonyLib; | ||||
| using TMPro; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace MuzikaGromche | ||||
| { | ||||
|     static class DeathScreenGameOverTextManager | ||||
|     { | ||||
|         private const string GameOverTextVanilla = "[LIFE SUPPORT: OFFLINE]"; | ||||
| 
 | ||||
|         public const string GameOverTextModdedDefault = "[ MUZIKA: GROMCHE ]"; | ||||
| 
 | ||||
|         public static void Clear() | ||||
|         { | ||||
|             SetTextImpl(GameOverTextVanilla); | ||||
|         } | ||||
| 
 | ||||
|         public static void SetText(string? text) | ||||
|         { | ||||
|             SetTextImpl(text ?? GameOverTextModdedDefault); | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerator SetTextAndClear(string? text) | ||||
|         { | ||||
|             SetText(text); | ||||
|             // Game Over animation duration is about 4.25 seconds | ||||
|             yield return new WaitForSeconds(5f); | ||||
|             Clear(); | ||||
|         } | ||||
| 
 | ||||
|         private static void SetTextImpl(string text) | ||||
|         { | ||||
|             GameObject textGameObject = GameObject.Find("Systems/UI/Canvas/DeathScreen/GameOverText"); | ||||
|             if (textGameObject == null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             TextMeshProUGUI tmp = textGameObject.GetComponent<TextMeshProUGUI>(); | ||||
|             if (tmp == null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             var transform = textGameObject.GetComponent<RectTransform>(); | ||||
|             if (transform == null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             // Default transform width is 645.8 which only fit the default message and no extra characters | ||||
|             Vector2 size = transform.sizeDelta; | ||||
|             if (Mathf.Approximately(size.x, 645.8f)) | ||||
|             { | ||||
|                 size.x += 100f; | ||||
|                 transform.sizeDelta = size; | ||||
|             } | ||||
|             tmp.text = text; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [HarmonyPatch(typeof(RoundManager))] | ||||
|     static class DeathScreenGameOverTextResetPatch | ||||
|     { | ||||
|         [HarmonyPatch(nameof(RoundManager.DespawnPropsAtEndOfRound))] | ||||
|         [HarmonyPatch(nameof(RoundManager.OnDestroy))] | ||||
|         [HarmonyPrefix] | ||||
|         static void OnDestroy(RoundManager __instance) | ||||
|         { | ||||
|             var _ = __instance; | ||||
|             DeathScreenGameOverTextManager.Clear(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -8,7 +8,7 @@ | |||
|         <AssemblyName>Ratijas.MuzikaGromche</AssemblyName> | ||||
|         <Product>Muzika Gromche</Product> | ||||
|         <Description>Add some content to your inverse teleporter experience on Titan!</Description> | ||||
|         <Version>1337.420.9004</Version> | ||||
|         <Version>1337.420.9003</Version> | ||||
|         <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|         <LangVersion>latest</LangVersion> | ||||
|         <Nullable>enable</Nullable> | ||||
|  | @ -52,15 +52,6 @@ | |||
|         <Reference Include="Unity.Netcode.Runtime" Publicize="true" Private="false"> | ||||
|             <HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\Unity.Netcode.Runtime.dll</HintPath> | ||||
|         </Reference> | ||||
|         <Reference Include="Unity.TextMeshPro" Publicize="true" Private="False"> | ||||
|             <HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\Unity.TextMeshPro.dll</HintPath>  | ||||
|         </Reference> | ||||
|         <Reference Include="UnityEngine.UI" Publicize="true" Private="False"> | ||||
|             <HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\UnityEngine.UI.dll</HintPath>  | ||||
|         </Reference> | ||||
|         <Reference Include="Unity.RenderPipelines.Core.Runtime" Publicize="true" Private="False"> | ||||
|             <HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll</HintPath>  | ||||
|         </Reference> | ||||
|     </ItemGroup> | ||||
| 
 | ||||
|     <ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'"> | ||||
|  |  | |||
|  | @ -13,7 +13,6 @@ using System.Net.NetworkInformation; | |||
| using System.Net.Sockets; | ||||
| using System.Reflection; | ||||
| using System.Security.Cryptography; | ||||
| using System.Text; | ||||
| using Unity.Netcode; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Networking; | ||||
|  | @ -57,9 +56,6 @@ namespace MuzikaGromche | |||
|                 ColorTransitionOut = 0.25f, | ||||
|                 ColorTransitionEasing = Easing.OutExpo, | ||||
|                 FlickerLightsTimeSeries = [-5, 29, 61], | ||||
|                 DrunknessLoopOffsetTimeSeries = new( | ||||
|                     [-2f, 0.0f, 1.0f, 03f, 30f,  32f,  33f, 35f, 62f], | ||||
|                     [ 0f, 0.4f, 0.6f, 0f,   0f, 0.5f, 0.7f, 0f,   0f]), | ||||
|                 Palette = Palette.Parse(["#B300FF", "#FFF100", "#00FF51", "#474747", "#FF00B3", "#0070FF"]), | ||||
|                 Lyrics = [ | ||||
|                     (-68, "Devchata pljashut pod spidami"), | ||||
|  | @ -136,9 +132,6 @@ namespace MuzikaGromche | |||
|                 ColorTransitionOut = 0.25f, | ||||
|                 ColorTransitionEasing = Easing.OutExpo, | ||||
|                 FlickerLightsTimeSeries = [-101, -93, -77, -61, -37, -5, 27], | ||||
|                 DrunknessLoopOffsetTimeSeries = new( | ||||
|                     [-48f, -46f, -42f, 16f, 19f, 23f], | ||||
|                     [  0f, 0.7f,   0f,  0f, 0.3f, 0f]), | ||||
|                 Palette = Palette.Parse(["#217F87", "#BAFF00", "#73BE25", "#78AB4E", "#FFFF00"]), | ||||
|                 Lyrics = [ | ||||
|                     (-111, "Deploy Destroy, porjadok eto otstoj"), | ||||
|  | @ -267,7 +260,6 @@ namespace MuzikaGromche | |||
|                 FadeOutDuration = 4, | ||||
|                 FlickerLightsTimeSeries = [-5, 31], | ||||
|                 Lyrics = [], | ||||
|                 GameOverText = "[MUZIKA GROMCHE: K-POP]", | ||||
|             }, | ||||
|             new SelectableAudioTrack | ||||
|             { | ||||
|  | @ -286,7 +278,6 @@ namespace MuzikaGromche | |||
|                 FadeOutDuration = 4, | ||||
|                 FlickerLightsTimeSeries = [-5], | ||||
|                 Lyrics = [], | ||||
|                 GameOverText = "[COULD'VE BEEN: IMMORTAL]", | ||||
|             }, | ||||
|             new SelectableAudioTrack | ||||
|             { | ||||
|  | @ -305,7 +296,6 @@ namespace MuzikaGromche | |||
|                 FadeOutDuration = 4, | ||||
|                 FlickerLightsTimeSeries = [-5.5f, 31, 63.9f], | ||||
|                 Lyrics = [], | ||||
|                 GameOverText = "[ HEY, YOUNG BLOOD ]", | ||||
|             }, | ||||
|             new SelectableAudioTrack | ||||
|             { | ||||
|  | @ -453,7 +443,6 @@ namespace MuzikaGromche | |||
|                     (96, $"\t\t\tresolving ur private IP\n/{PwnLyricsVariants[^1]}"), | ||||
|                     (98, $"\t\t\tresolving ur private IP\nP_WNED"), | ||||
|                 ], | ||||
|                 GameOverText = "[HACK3D BY: RUSSI4NS]", | ||||
|             }, | ||||
|             new SelectableAudioTrack | ||||
|             { | ||||
|  | @ -478,7 +467,6 @@ namespace MuzikaGromche | |||
|                 FadeOutDuration = 6, | ||||
|                 FlickerLightsTimeSeries = [-120.5f, -105, -89, -8, 44, 45], | ||||
|                 Lyrics = [], | ||||
|                 GameOverText = "[DIDN'T PUMP IT: LOUDER]", | ||||
|             }, | ||||
|             new SelectableAudioTrack | ||||
|             { | ||||
|  | @ -590,57 +578,6 @@ namespace MuzikaGromche | |||
|                 FlickerLightsTimeSeries = [-68.5f, -16.5f, 30.5f], | ||||
|                 Lyrics = [], | ||||
|             }, | ||||
|             new SelectableTracksGroup | ||||
|             { | ||||
|                 Name = "AttentionPls", | ||||
|                 Language = Language.RUSSIAN, | ||||
|                 IsExplicit = true, | ||||
|                 Tracks = | ||||
|                 [ | ||||
|                     new SelectableAudioTrack | ||||
|                     { | ||||
|                         Name = "AttentionPls1", | ||||
|                         FileNameLoop = "AttentionPlsLoop.ogg", | ||||
|                         AudioType = AudioType.OGGVORBIS, | ||||
|                         Language = Language.RUSSIAN, | ||||
|                         WindUpTimer = 39.19f, | ||||
|                         Bars = 8, | ||||
|                         BeatsOffset = 0.3f, | ||||
|                         ColorTransitionIn = 0.4f, | ||||
|                         ColorTransitionOut = 0.4f, | ||||
|                         ColorTransitionEasing = Easing.OutExpo, | ||||
|                         Palette = Palette.Parse(["#FCEB3C", "#FC3C9D", "#65C7FA", "#89FC8F", "#FEE9E9", "#FCEB3C", "#89FC8F", "#FC3C9D"]), | ||||
|                         LoopOffset = 0, | ||||
|                         FadeOutBeat = -6, | ||||
|                         FadeOutDuration = 5, | ||||
|                         FlickerLightsTimeSeries = [-8, 31], | ||||
|                         Lyrics = [], | ||||
|                         DrunknessLoopOffsetTimeSeries = new([7f, 12f, 15f], [0f, 0.90f, 0f]), | ||||
|                         CondensationLoopOffsetTimeSeries = new([23f, 28f, 31f], [0f, 0.4f, 0f]), | ||||
|                     }, | ||||
|                     new SelectableAudioTrack | ||||
|                     { | ||||
|                         Name = "AttentionPls2", | ||||
|                         FileNameLoop = "AttentionPlsLoop.ogg", | ||||
|                         AudioType = AudioType.OGGVORBIS, | ||||
|                         Language = Language.RUSSIAN, | ||||
|                         WindUpTimer = 39.19f, | ||||
|                         Bars = 8, | ||||
|                         BeatsOffset = 0.3f, | ||||
|                         ColorTransitionIn = 0.4f, | ||||
|                         ColorTransitionOut = 0.4f, | ||||
|                         ColorTransitionEasing = Easing.OutExpo, | ||||
|                         Palette = Palette.Parse(["#FCEB3C", "#FC3C9D", "#65C7FA", "#89FC8F", "#FEE9E9", "#FCEB3C", "#89FC8F", "#FC3C9D"]), | ||||
|                         LoopOffset = 0, | ||||
|                         FadeOutBeat = -6, | ||||
|                         FadeOutDuration = 5, | ||||
|                         FlickerLightsTimeSeries = [-8, 31], | ||||
|                         Lyrics = [], | ||||
|                         DrunknessLoopOffsetTimeSeries = new([7f, 12f, 15f], [0f, 0.90f, 0f]), | ||||
|                         CondensationLoopOffsetTimeSeries = new([23f, 28f, 31f], [0f, 0.4f, 0f]), | ||||
|                     }, | ||||
|                 ], | ||||
|             }, | ||||
|         ]; | ||||
| 
 | ||||
|         public static ISelectableTrack ChooseTrack() | ||||
|  | @ -743,8 +680,6 @@ namespace MuzikaGromche | |||
|                 harmony.PatchAll(typeof(DiscoBallTilePatch)); | ||||
|                 harmony.PatchAll(typeof(DiscoBallDespawnPatch)); | ||||
|                 harmony.PatchAll(typeof(SpawnRatePatch)); | ||||
|                 harmony.PatchAll(typeof(DeathScreenGameOverTextResetPatch)); | ||||
|                 harmony.PatchAll(typeof(ScreenFiltersManager.HUDManagerScreenFiltersPatch)); | ||||
|                 NetcodePatcher(); | ||||
|                 Compatibility.Register(this); | ||||
|             } | ||||
|  | @ -854,35 +789,6 @@ namespace MuzikaGromche | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public readonly struct TimeSeries<T> | ||||
|     { | ||||
|         public TimeSeries() : this([], []) { } | ||||
| 
 | ||||
|         public TimeSeries(float[] beats, T[] values) | ||||
|         { | ||||
|             if (beats.Length != values.Length) | ||||
|             { | ||||
|                 throw new ArgumentOutOfRangeException($"Time series length mismatch: {beats.Length} != {values.Length}"); | ||||
|             } | ||||
|             var dict = new SortedDictionary<float, T>(); | ||||
|             for (int i = 0; i < values.Length; i++) | ||||
|             { | ||||
|                 dict.Add(beats[i], values[i]); | ||||
|             } | ||||
|             Beats = [.. dict.Keys]; | ||||
|             Values = [.. dict.Values]; | ||||
|         } | ||||
| 
 | ||||
|         public readonly int Length => Beats.Length; | ||||
|         public readonly float[] Beats { get; } = []; | ||||
|         public readonly T[] Values { get; } = []; | ||||
| 
 | ||||
|         public override string ToString() | ||||
|         { | ||||
|             return $"{nameof(TimeSeries<T>)}([{string.Join(", ", Beats)}], [{string.Join(", ", Values)}])"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // An instance of a track which appears as a configuration entry and | ||||
|     // can be selected using weighted random from a list of selectable tracks. | ||||
|     public interface ISelectableTrack | ||||
|  | @ -975,12 +881,7 @@ namespace MuzikaGromche | |||
|         // If the chosen alternative is an empty string, lyrics event shall be skipped. | ||||
|         public string[] LyricsLines { get; } | ||||
| 
 | ||||
|         public TimeSeries<float> DrunknessLoopOffsetTimeSeries { get; } | ||||
|         public TimeSeries<float> CondensationLoopOffsetTimeSeries { get; } | ||||
| 
 | ||||
|         public Palette Palette { get; } | ||||
| 
 | ||||
|         public string? GameOverText { get => null; } | ||||
|     } | ||||
| 
 | ||||
|     // A proxy audio track with default implementation for every IAudioTrack method that simply forwards requests to the inner IAudioTrack. | ||||
|  | @ -1005,10 +906,7 @@ namespace MuzikaGromche | |||
|         float[] IAudioTrack.FlickerLightsTimeSeries => Track.FlickerLightsTimeSeries; | ||||
|         float[] IAudioTrack.LyricsTimeSeries => Track.LyricsTimeSeries; | ||||
|         string[] IAudioTrack.LyricsLines => Track.LyricsLines; | ||||
|         TimeSeries<float> IAudioTrack.DrunknessLoopOffsetTimeSeries => Track.DrunknessLoopOffsetTimeSeries; | ||||
|         TimeSeries<float> IAudioTrack.CondensationLoopOffsetTimeSeries => Track.CondensationLoopOffsetTimeSeries; | ||||
|         Palette IAudioTrack.Palette => Track.Palette; | ||||
|         string? IAudioTrack.GameOverText => Track.GameOverText; | ||||
|     } | ||||
| 
 | ||||
|     // Core audio track implementation with some defaults and config overrides. | ||||
|  | @ -1083,12 +981,7 @@ namespace MuzikaGromche | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public TimeSeries<float> DrunknessLoopOffsetTimeSeries { get; init; } = new(); | ||||
|         public TimeSeries<float> CondensationLoopOffsetTimeSeries { get; init; } = new(); | ||||
| 
 | ||||
|         public Palette Palette { get; set; } = Palette.DEFAULT; | ||||
| 
 | ||||
|         public string? GameOverText { get; init; } = null; | ||||
|     } | ||||
| 
 | ||||
|     // Standalone, top-level, selectable audio track | ||||
|  | @ -1294,19 +1187,9 @@ namespace MuzikaGromche | |||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         public readonly float Duration(bool longest = false) | ||||
|         public readonly float Duration() | ||||
|         { | ||||
|             if (longest) | ||||
|             { | ||||
|                 var to = BeatToInclusive; | ||||
|                 if (BeatFromExclusive >= 0f && BeatToInclusive >= 0f && to < BeatFromExclusive) | ||||
|                 { | ||||
|                     // wrapped | ||||
|                     to += LoopBeats; | ||||
|                 } | ||||
|                 return Mathf.Max(0f, to - BeatFromExclusive); | ||||
|             } | ||||
|             else if (IsEmpty()) | ||||
|             if (IsEmpty()) | ||||
|             { | ||||
|                 return 0f; | ||||
|             } | ||||
|  | @ -1561,8 +1444,8 @@ namespace MuzikaGromche | |||
| 
 | ||||
|             if (AudioState.HasStarted) | ||||
|             { | ||||
|                 var loopOffsetTimestamp = Update(LoopLoopingState); | ||||
|                 var loopOffsetSpan = BeatTimeSpan.Between(LastKnownLoopOffsetBeat, loopOffsetTimestamp); | ||||
|                 var loopTimestamp = Update(LoopLoopingState); | ||||
|                 var loopOffsetSpan = BeatTimeSpan.Between(LastKnownLoopOffsetBeat, loopTimestamp); | ||||
| 
 | ||||
|                 // Do not go back in time | ||||
|                 if (!loopOffsetSpan.IsEmpty()) | ||||
|  | @ -1573,8 +1456,8 @@ namespace MuzikaGromche | |||
|                     } | ||||
| 
 | ||||
|                     var windUpOffsetTimestamp = Update(WindUpLoopingState); | ||||
|                     LastKnownLoopOffsetBeat = loopOffsetTimestamp.Beat; | ||||
|                     var events = GetEvents(loopOffsetTimestamp, loopOffsetSpan, windUpOffsetTimestamp); | ||||
|                     LastKnownLoopOffsetBeat = loopTimestamp.Beat; | ||||
|                     var events = GetEvents(loopOffsetSpan, windUpOffsetTimestamp); | ||||
| #if DEBUG | ||||
|                     Debug.Log($"{nameof(MuzikaGromche)} looping? {(LoopLoopingState.IsLooping ? 'X' : '_')}{(WindUpLoopingState.IsLooping ? 'X' : '_')} Loop={loopOffsetSpan} WindUp={windUpOffsetTimestamp} Time={Time.realtimeSinceStartup:N4} events={string.Join(",", events)}"); | ||||
| #endif | ||||
|  | @ -1590,13 +1473,13 @@ namespace MuzikaGromche | |||
|             return loopingState.Update(AudioState.Time, AudioState.IsExtrapolated, AdditionalOffset()); | ||||
|         } | ||||
| 
 | ||||
|         // Timings that may be changed through config | ||||
|         // Timings that may be changes through config | ||||
|         private float AdditionalOffset() | ||||
|         { | ||||
|             return Config.AudioOffset.Value + track.BeatsOffsetInSeconds; | ||||
|         } | ||||
| 
 | ||||
|         private List<BaseEvent> GetEvents(BeatTimestamp loopOffsetTimestamp, BeatTimeSpan loopOffsetSpan, BeatTimestamp windUpOffsetTimestamp) | ||||
|         private List<BaseEvent> GetEvents(BeatTimeSpan loopOffsetSpan, BeatTimestamp windUpOffsetTimestamp) | ||||
|         { | ||||
|             List<BaseEvent> events = []; | ||||
| 
 | ||||
|  | @ -1617,6 +1500,7 @@ namespace MuzikaGromche | |||
|             } | ||||
| 
 | ||||
|             // TODO: quick editor | ||||
|             // loopOffsetSpan.GetLastIndex(Config.LyricsTimeSeries) | ||||
|             if (Config.DisplayLyrics.Value) | ||||
|             { | ||||
|                 var index = loopOffsetSpan.GetLastIndex(track.LyricsTimeSeries); | ||||
|  | @ -1633,16 +1517,6 @@ namespace MuzikaGromche | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (GetInterpolation(loopOffsetTimestamp, track.DrunknessLoopOffsetTimeSeries, Easing.Linear) is { } drunkness) | ||||
|             { | ||||
|                 events.Add(new DrunkEvent(drunkness)); | ||||
|             } | ||||
| 
 | ||||
|             if (GetInterpolation(loopOffsetTimestamp, track.CondensationLoopOffsetTimeSeries, Easing.Linear) is { } condensation) | ||||
|             { | ||||
|                 events.Add(new CondensationEvent(condensation)); | ||||
|             } | ||||
| 
 | ||||
|             return events; | ||||
|         } | ||||
| 
 | ||||
|  | @ -1746,84 +1620,6 @@ namespace MuzikaGromche | |||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private float? GetInterpolation(BeatTimestamp timestamp, TimeSeries<float> timeSeries, Easing easing) | ||||
|         { | ||||
|             if (timeSeries.Length == 0) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             else if (timeSeries.Length == 1) | ||||
|             { | ||||
|                 return timeSeries.Values[0]; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 int? indexOfPrevious = null; | ||||
|                 // Find index of the previous time. If looped, wrap backwards. In either case it is possibly missing. | ||||
|                 for (int i = timeSeries.Length - 1; i >= 0; i--) | ||||
|                 { | ||||
|                     if (timeSeries.Beats[i] <= timestamp.Beat) | ||||
|                     { | ||||
|                         indexOfPrevious = i; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (indexOfPrevious == null && timestamp.IsLooping) | ||||
|                 { | ||||
|                     indexOfPrevious = timeSeries.Length - 1; | ||||
|                 } | ||||
| 
 | ||||
|                 // Find index of the next time. If looped, wrap forward. | ||||
|                 int? indexOfNext = null; | ||||
|                 for (int i = 0; i < timeSeries.Length; i++) | ||||
|                 { | ||||
|                     if (timeSeries.Beats[i] >= timestamp.Beat) | ||||
|                     { | ||||
|                         indexOfNext = i; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (indexOfNext == null && timestamp.IsLooping) | ||||
|                 { | ||||
|                     for (int i = 0; i < timeSeries.Length; i++) | ||||
|                     { | ||||
|                         if (timeSeries.Beats[i] >= 0f) | ||||
|                         { | ||||
|                             indexOfNext = i; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 switch (indexOfPrevious, indexOfNext) | ||||
|                 { | ||||
|                     case (null, null): | ||||
|                         return null; | ||||
| 
 | ||||
|                     case (null, { } index): | ||||
|                         return timeSeries.Values[index]; | ||||
| 
 | ||||
|                     case ({ } index, null): | ||||
|                         return timeSeries.Values[index]; | ||||
| 
 | ||||
|                     case ({ } prev, { } next) when prev == next || timeSeries.Beats[prev] == timeSeries.Beats[next]: | ||||
|                         return timeSeries.Values[prev]; | ||||
| 
 | ||||
|                     case ({ } prev, { } next): | ||||
|                         var prevBeat = timeSeries.Beats[prev]; | ||||
|                         var nextBeat = timeSeries.Beats[next]; | ||||
|                         var prevTimestamp = new BeatTimestamp(timestamp.LoopBeats, isLooping: false, prevBeat, false); | ||||
|                         var nextTimestamp = new BeatTimestamp(timestamp.LoopBeats, isLooping: false, nextBeat, false); | ||||
|                         var t = BeatTimeSpan.Between(prevTimestamp, timestamp).Duration(longest: true) | ||||
|                             / BeatTimeSpan.Between(prevTimestamp, nextTimestamp).Duration(longest: true); | ||||
|                         var prevVal = timeSeries.Values[prev]; | ||||
|                         var nextVal = timeSeries.Values[next]; | ||||
|                         var val = Mathf.Lerp(prevVal, nextVal, easing.Eval(t)); | ||||
|                         return val; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     abstract class BaseEvent; | ||||
|  | @ -1899,20 +1695,6 @@ namespace MuzikaGromche | |||
|         public override string ToString() => "WindUp"; | ||||
|     } | ||||
| 
 | ||||
|     abstract class HUDEvent : BaseEvent; | ||||
| 
 | ||||
|     class DrunkEvent(float drunkness) : HUDEvent | ||||
|     { | ||||
|         public readonly float Drunkness = drunkness; | ||||
|         public override string ToString() => $"Drunk({Drunkness:N2})"; | ||||
|     } | ||||
| 
 | ||||
|     class CondensationEvent(float condensation) : HUDEvent | ||||
|     { | ||||
|         public readonly float Condensation = condensation; | ||||
|         public override string ToString() => $"Condensation({Condensation:N2})"; | ||||
|     } | ||||
| 
 | ||||
|     // Default C#/.NET remainder operator % returns negative result for negative input | ||||
|     // which is unsuitable as an index for an array. | ||||
|     static class Mod | ||||
|  | @ -2047,8 +1829,6 @@ namespace MuzikaGromche | |||
|         private static string? ColorTransitionEasingOverride = null; | ||||
|         private static float[]? FlickerLightsTimeSeriesOverride = null; | ||||
|         private static float[]? LyricsTimeSeriesOverride = null; | ||||
|         private static TimeSeries<float>? DrunknessLoopOffsetTimeSeriesOverride = null; | ||||
|         private static TimeSeries<float>? CondensationLoopOffsetTimeSeriesOverride = null; | ||||
|         private static Palette? PaletteOverride = null; | ||||
| 
 | ||||
|         private class AudioTrackWithConfigOverride(IAudioTrack track) : ProxyAudioTrack(track), IAudioTrack | ||||
|  | @ -2073,9 +1853,6 @@ namespace MuzikaGromche | |||
| 
 | ||||
|             float[] IAudioTrack.LyricsTimeSeries => LyricsTimeSeriesOverride ?? Track.LyricsTimeSeries; | ||||
| 
 | ||||
|             TimeSeries<float> IAudioTrack.DrunknessLoopOffsetTimeSeries => DrunknessLoopOffsetTimeSeriesOverride ?? Track.DrunknessLoopOffsetTimeSeries; | ||||
|             TimeSeries<float> IAudioTrack.CondensationLoopOffsetTimeSeries => CondensationLoopOffsetTimeSeriesOverride ?? Track.CondensationLoopOffsetTimeSeries; | ||||
| 
 | ||||
|             Palette IAudioTrack.Palette => PaletteOverride ?? Track.Palette; | ||||
|         } | ||||
| #endif | ||||
|  | @ -2100,8 +1877,6 @@ namespace MuzikaGromche | |||
|             LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(OverrideSpawnRates, Default(new BoolCheckBoxOptions()))); | ||||
| 
 | ||||
| #if DEBUG | ||||
|             SetupEntriesForGameOverText(configFile); | ||||
|             SetupEntriesForScreenFilters(configFile); | ||||
|             SetupEntriesForExtrapolation(configFile); | ||||
|             SetupEntriesToSkipWinding(configFile); | ||||
|             SetupEntriesForPaletteOverride(configFile); | ||||
|  | @ -2296,8 +2071,6 @@ namespace MuzikaGromche | |||
|             ConfigEntry<float> fadeOutDurationEntry = null!; | ||||
|             ConfigEntry<string> flickerLightsTimeSeriesEntry = null!; | ||||
|             ConfigEntry<string> lyricsTimeSeriesEntry = null!; | ||||
|             ConfigEntry<string> drunknessTimeSeriesEntry = null!; | ||||
|             ConfigEntry<string> condensationTimeSeriesEntry = null!; | ||||
|             ConfigEntry<float> beatsOffsetEntry = null!; | ||||
|             ConfigEntry<float> colorTransitionInEntry = null!; | ||||
|             ConfigEntry<float> colorTransitionOutEntry = null!; | ||||
|  | @ -2318,13 +2091,9 @@ namespace MuzikaGromche | |||
|             fadeOutDurationEntry = configFile.Bind(section, "Fade Out Duration", 0f, | ||||
|                 new ConfigDescription("Duration of fading out", new AcceptableValueRange<float>(0, 10))); | ||||
|             flickerLightsTimeSeriesEntry = configFile.Bind(section, "Flicker Lights Time Series", "", | ||||
|                 new ConfigDescription("Time series of loop offset beats when to flicker the lights.")); | ||||
|                 new ConfigDescription("Time series of beat offsets when to flicker the lights.")); | ||||
|             lyricsTimeSeriesEntry = configFile.Bind(section, "Lyrics Time Series", "", | ||||
|                 new ConfigDescription("Time series of loop offset beats when to show lyrics lines.")); | ||||
|             drunknessTimeSeriesEntry = configFile.Bind(section, "Drunkness", "", | ||||
|                 new ConfigDescription("Time series of loop offset beats which are keyframes for the drunkness effect. Format: 'time1: value1, time2: value2")); | ||||
|             condensationTimeSeriesEntry = configFile.Bind(section, "Helmet Condensation Drops", "", | ||||
|                 new ConfigDescription("Time series of loop offset beats which are keyframes for the Helmet Condensation Drops effect. Format: 'time1: value1, time2: value2")); | ||||
|                 new ConfigDescription("Time series of beat offsets when to show lyrics lines.")); | ||||
|             beatsOffsetEntry = configFile.Bind(section, "Beats Offset", 0f, | ||||
|                 new ConfigDescription("How much to offset the whole beat. More is later", new AcceptableValueRange<float>(-0.5f, 0.5f))); | ||||
|             colorTransitionInEntry = configFile.Bind(section, "Color Transition In", 0.25f, | ||||
|  | @ -2339,8 +2108,6 @@ namespace MuzikaGromche | |||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(fadeOutDurationEntry, floatSliderOptions)); | ||||
|             LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(flickerLightsTimeSeriesEntry, Default(new TextInputFieldOptions()))); | ||||
|             LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(lyricsTimeSeriesEntry, Default(new TextInputFieldOptions()))); | ||||
|             LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(drunknessTimeSeriesEntry, Default(new TextInputFieldOptions()))); | ||||
|             LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(condensationTimeSeriesEntry, Default(new TextInputFieldOptions()))); | ||||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(beatsOffsetEntry, floatSliderOptions)); | ||||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionInEntry, floatSliderOptions)); | ||||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionOutEntry, floatSliderOptions)); | ||||
|  | @ -2350,8 +2117,6 @@ namespace MuzikaGromche | |||
|             registerStruct(fadeOutDurationEntry, t => t.FadeOutDuration, x => FadeOutDurationOverride = x); | ||||
|             registerArray(flickerLightsTimeSeriesEntry, t => t.FlickerLightsTimeSeries, xs => FlickerLightsTimeSeriesOverride = xs, float.Parse, sort: true); | ||||
|             registerArray(lyricsTimeSeriesEntry, t => t.LyricsTimeSeries, xs => LyricsTimeSeriesOverride = xs, float.Parse, sort: true); | ||||
|             registerTimeSeries(drunknessTimeSeriesEntry, t => t.DrunknessLoopOffsetTimeSeries, xs => DrunknessLoopOffsetTimeSeriesOverride = xs, float.Parse, f => f.ToString()); | ||||
|             registerTimeSeries(condensationTimeSeriesEntry, t => t.CondensationLoopOffsetTimeSeries, xs => CondensationLoopOffsetTimeSeriesOverride = xs, float.Parse, f => f.ToString()); | ||||
|             registerStruct(beatsOffsetEntry, t => t.BeatsOffset, x => BeatsOffsetOverride = x); | ||||
|             registerStruct(colorTransitionInEntry, t => t.ColorTransitionIn, x => ColorTransitionInOverride = x); | ||||
|             registerStruct(colorTransitionOutEntry, t => t.ColorTransitionOut, x => ColorTransitionOutOverride = x); | ||||
|  | @ -2385,76 +2150,7 @@ namespace MuzikaGromche | |||
|                         } | ||||
|                         setter.Invoke(overrideTimingsEntry.Value ? values : null); | ||||
|                     }); | ||||
|             void registerTimeSeries<T>(ConfigEntry<string> entry, Func<IAudioTrack, TimeSeries<T>> getter, Action<TimeSeries<T>?> setter, Func<string, T> parser, Func<T, string> formatter) => | ||||
|                 register(entry, | ||||
|                     (track) => | ||||
|                     { | ||||
|                         var ts = getter(track); | ||||
|                         return formatTimeSeries(ts, formatter); | ||||
|                     }, | ||||
|                     () => | ||||
|                     { | ||||
|                         var ts = parseTimeSeries(entry.Value, parser); | ||||
|                         if (ts is { } timeSeries) | ||||
|                         { | ||||
|                             entry.Value = formatTimeSeries(timeSeries, formatter); | ||||
|                         } | ||||
|                         setter.Invoke(overrideTimingsEntry.Value ? ts : null); | ||||
|                     }); | ||||
| 
 | ||||
|             // current restriction is that formatted value can not contain commas or semicolons. | ||||
|             TimeSeries<T>? parseTimeSeries<T>(string str, Func<string, T> parser) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (string.IsNullOrWhiteSpace(str)) | ||||
|                     { | ||||
|                         return null; | ||||
|                     } | ||||
| 
 | ||||
|                     List<float> beats = []; | ||||
|                     List<T> values = []; | ||||
|                     foreach (var pair in str.Split(",")) | ||||
|                     { | ||||
|                         if (string.IsNullOrWhiteSpace(pair)) | ||||
|                         { | ||||
|                             continue; | ||||
|                         } | ||||
|                         var keyvalue = pair.Split(":"); | ||||
|                         if (keyvalue.Length != 2) | ||||
|                         { | ||||
|                             throw new FormatException($"Pair must be separated by exactly one semicolon: '{pair}'"); | ||||
|                         } | ||||
|                         var beat = float.Parse(keyvalue[0].Trim()); | ||||
|                         var val = parser(keyvalue[1].Trim()); | ||||
|                         beats.Add(beat); | ||||
|                         values.Add(val); | ||||
|                     } | ||||
|                     var ts = new TimeSeries<T>(beats.ToArray(), values.ToArray()); | ||||
|                     return ts; | ||||
|                 } | ||||
|                 catch (Exception e) | ||||
|                 { | ||||
|                     Debug.Log($"{nameof(MuzikaGromche)} Unable to parse time series: {e}"); | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
|             string formatTimeSeries<T>(TimeSeries<T> ts, Func<T, string> formatter) | ||||
|             { | ||||
|                 StringBuilder strings = new(); | ||||
|                 for (int i = 0; i < ts.Length; i++) | ||||
|                 { | ||||
|                     var beat = ts.Beats[i]; | ||||
|                     var value = formatter(ts.Values[i]); | ||||
|                     strings.Append($"{beat}: {value}"); | ||||
|                     if (i != ts.Length - 1) | ||||
|                     { | ||||
|                         strings.Append(", "); | ||||
|                     } | ||||
|                 } | ||||
|                 Debug.Log($"{nameof(MuzikaGromche)} format time series {ts} {strings}"); | ||||
|                 return strings.ToString(); | ||||
|             } | ||||
|             T[]? parseStringArray<T>(string str, Func<string, T> parser, bool sort = false) where T : struct | ||||
|             { | ||||
|                 try | ||||
|  | @ -2486,59 +2182,6 @@ namespace MuzikaGromche | |||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void SetupEntriesForGameOverText(ConfigFile configFile) | ||||
|         { | ||||
|             const string section = "Game Over"; | ||||
|             var gameOverTextConfigEntry = configFile.Bind(section, "Game Over Text", DeathScreenGameOverTextManager.GameOverTextModdedDefault, | ||||
|                 new ConfigDescription("Custom Game Over text to show.")); | ||||
|             LethalConfigManager.AddConfigItem(new GenericButtonConfigItem(section, "Game Over Animation", | ||||
|                 "Run Death Screen / Game Over animation 3 times.", "Trigger", () => | ||||
|                 { | ||||
|                     HUDManager.Instance.StartCoroutine(AnimateGameOverText(gameOverTextConfigEntry.Value)); | ||||
|                 })); | ||||
|             LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(gameOverTextConfigEntry, requiresRestart: false)); | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerator AnimateGameOverText(string text) | ||||
|         { | ||||
|             yield return new WaitForSeconds(1f); | ||||
|             for (int i = 0; i < 3; i++) | ||||
|             { | ||||
|                 DeathScreenGameOverTextManager.SetText(text); | ||||
|                 HUDManager.Instance.gameOverAnimator.SetTrigger("gameOver"); | ||||
|                 yield return new WaitForSeconds(5f); | ||||
|                 HUDManager.Instance.gameOverAnimator.SetTrigger("revive"); | ||||
|                 yield return new WaitForSeconds(1f); | ||||
|             } | ||||
|             DeathScreenGameOverTextManager.Clear(); | ||||
|         } | ||||
| 
 | ||||
|         private void SetupEntriesForScreenFilters(ConfigFile configFile) | ||||
|         { | ||||
|             const string section = "Screen Filters"; | ||||
| 
 | ||||
|             var drunkConfigEntry = configFile.Bind(section, "Drunkness Level", 0f, | ||||
|                 new ConfigDescription("Override drunkness level in Screen Filters Manager.")); | ||||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(drunkConfigEntry, requiresRestart: false)); | ||||
|             drunkConfigEntry.SettingChanged += (sender, args) => | ||||
|             { | ||||
|                 ScreenFiltersManager.Drunkness = drunkConfigEntry.Value; | ||||
|             }; | ||||
| 
 | ||||
|             var condensationConfigEntry = configFile.Bind(section, "Condensation Level", 0f, | ||||
|                 new ConfigDescription("Override drunkness level in Screen Filters Manager.")); | ||||
|             LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(condensationConfigEntry, new FloatSliderOptions() | ||||
|             { | ||||
|                 Min = 0f, | ||||
|                 Max = 0.27f, | ||||
|                 RequiresRestart = false, | ||||
|             })); | ||||
|             condensationConfigEntry.SettingChanged += (sender, args) => | ||||
|             { | ||||
|                 ScreenFiltersManager.HelmetCondensationDrops = condensationConfigEntry.Value; | ||||
|             }; | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         private T Default<T>(T options) where T : BaseOptions | ||||
|  | @ -2692,16 +2335,6 @@ namespace MuzikaGromche | |||
|             LoopAudioSource.PlayScheduled(loopStartDspTime); | ||||
|             Debug.Log($"{nameof(MuzikaGromche)} Play Intro: dspTime={AudioSettings.dspTime:N4}, intro.time={IntroAudioSource.time:N4}/{IntroAudioSource.clip.length:N4}, scheduled Loop={loopStartDspTime}"); | ||||
|         } | ||||
| 
 | ||||
|         public void OverrideDeathScreenGameOverText() | ||||
|         { | ||||
|             if (CurrentTrack == null) | ||||
|             { | ||||
|                 // Playing as a client with a host who doesn't have the mod | ||||
|                 return; | ||||
|             } | ||||
|             StartCoroutine(DeathScreenGameOverTextManager.SetTextAndClear(CurrentTrack.GameOverText)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [HarmonyPatch(typeof(JesterAI))] | ||||
|  | @ -2816,7 +2449,6 @@ namespace MuzikaGromche | |||
|             { | ||||
|                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||
|                 DiscoBallManager.Disable(); | ||||
|                 ScreenFiltersManager.Clear(); | ||||
|                 // Rotate track groups | ||||
|                 behaviour.ChooseTrackServerRpc(); | ||||
|                 behaviour.BeatTimeState = null; | ||||
|  | @ -2826,7 +2458,6 @@ namespace MuzikaGromche | |||
|             else if ((__instance.previousState == 1 || __instance.previousState == 2) && behaviour.BeatTimeState is { } beatTimeState) | ||||
|             { | ||||
|                 var events = beatTimeState.Update(introAudioSource, loopAudioSource); | ||||
|                 var localPlayerCanHearMusic = Plugin.LocalPlayerCanHearMusic(__instance); | ||||
|                 foreach (var ev in events) | ||||
|                 { | ||||
|                     switch (ev) | ||||
|  | @ -2843,33 +2474,16 @@ namespace MuzikaGromche | |||
|                             RoundManager.Instance.FlickerLights(true); | ||||
|                             break; | ||||
| 
 | ||||
|                         case LyricsEvent e when localPlayerCanHearMusic: | ||||
|                         case LyricsEvent e: | ||||
|                             if (Plugin.LocalPlayerCanHearMusic(__instance)) | ||||
|                             { | ||||
|                                 Plugin.DisplayLyrics(e.Text); | ||||
|                             break; | ||||
| 
 | ||||
|                         case DrunkEvent e when localPlayerCanHearMusic: | ||||
|                             ScreenFiltersManager.Drunkness = e.Drunkness; | ||||
|                             break; | ||||
| 
 | ||||
|                         case CondensationEvent e when localPlayerCanHearMusic: | ||||
|                             ScreenFiltersManager.HelmetCondensationDrops = e.Condensation; | ||||
|                             } | ||||
|                             break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [HarmonyPatch(nameof(JesterAI.killPlayerAnimation))] | ||||
|         [HarmonyPrefix] | ||||
|         static void JesterKillPlayerAnimationPrefix(JesterAI __instance, int playerId) | ||||
|         { | ||||
|             // Note on cast to int: base game already downcasts ulong to int anyway | ||||
|             if (playerId == (int)GameNetworkManager.Instance.localPlayerController.playerClientId) | ||||
|             { | ||||
|                 var behaviour = __instance.GetComponent<MuzikaGromcheJesterNetworkBehaviour>(); | ||||
|                 behaviour.OverrideDeathScreenGameOverText(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [HarmonyPatch(typeof(EnemyAI))] | ||||
|  | @ -2885,10 +2499,8 @@ namespace MuzikaGromche | |||
|             { | ||||
|                 PoweredLightsBehaviour.Instance.ResetLightColor(); | ||||
|                 DiscoBallManager.Disable(); | ||||
|                 DeathScreenGameOverTextManager.Clear(); | ||||
|                 ScreenFiltersManager.Clear(); | ||||
|                 // Just in case if players have spawned multiple Jesters, | ||||
|                 // Don't reset Config.CurrentTrack to null, | ||||
|                 // Don't reset Plugin.CurrentTrack to null, | ||||
|                 // so that the latest chosen track remains set. | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -1,146 +0,0 @@ | |||
| using System.Collections; | ||||
| using HarmonyLib; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace MuzikaGromche | ||||
| { | ||||
|     static class ScreenFiltersManager | ||||
|     { | ||||
|         private const float VibilityThreshold = 0.01f; | ||||
| 
 | ||||
|         private static bool drunknessChangedThisFrame = false; | ||||
|         private static float drunkness = 0f; | ||||
|         public static float Drunkness | ||||
|         { | ||||
|             get => drunkness; | ||||
|             set | ||||
|             { | ||||
|                 drunkness = value; | ||||
|                 drunknessChangedThisFrame = true; | ||||
|                 ScheduleUpdate(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static bool helmetCondensationDropsChangedThisFrame = false; | ||||
|         private static float helmetCondensationDrops = 0f; | ||||
|         public static float HelmetCondensationDrops | ||||
|         { | ||||
|             get => helmetCondensationDrops; | ||||
|             set | ||||
|             { | ||||
|                 helmetCondensationDrops = value; | ||||
|                 helmetCondensationDropsChangedThisFrame = true; | ||||
|                 ScheduleUpdate(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static Coroutine? scheduledUpdate = null; | ||||
| 
 | ||||
|         private static void ScheduleUpdate() | ||||
|         { | ||||
|             CancelScheduledUpdate(); | ||||
|             scheduledUpdate = HUDManager.Instance.StartCoroutine(ScheduledUpdate()); | ||||
|         } | ||||
| 
 | ||||
|         private static void CancelScheduledUpdate() | ||||
|         { | ||||
|             if (scheduledUpdate != null) | ||||
|             { | ||||
|                 HUDManager.Instance.StopCoroutine(scheduledUpdate); | ||||
|                 scheduledUpdate = null; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static IEnumerator ScheduledUpdate() | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 yield return new WaitForEndOfFrame(); | ||||
|                 Update(); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 scheduledUpdate = null; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static void Update() | ||||
|         { | ||||
|             CancelScheduledUpdate(); | ||||
|             var hud = HUDManager.Instance; | ||||
| 
 | ||||
|             if (!drunknessChangedThisFrame) | ||||
|             { | ||||
|                 // animated roll-off | ||||
|                 drunkness = Mathf.Clamp(drunkness - Time.deltaTime / 2f, 0f, 1f); | ||||
|             } | ||||
|             drunknessChangedThisFrame = false; | ||||
|             if (drunkness > VibilityThreshold) | ||||
|             { | ||||
|                 var moddedDrunknessFilterWeight = StartOfRound.Instance.drunknessSideEffect.Evaluate(drunkness); | ||||
|                 var moddedGasImageAlphaAlpha = moddedDrunknessFilterWeight * 1.5f; | ||||
|                 // set the final value to the greatest of the two, so that we don't accidentally undo TZP's visual effect. | ||||
|                 hud.drunknessFilter.weight = Mathf.Max(hud.drunknessFilter.weight, moddedDrunknessFilterWeight); | ||||
|                 hud.gasImageAlpha.alpha = Mathf.Max(hud.gasImageAlpha.alpha, moddedGasImageAlphaAlpha); | ||||
|                 // Image alpha only makes sense if the animator is running | ||||
|                 hud.gasHelmetAnimator.SetBool("gasEmitting", value: true); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ClearDrunkness(); | ||||
|             } | ||||
| 
 | ||||
|             if (!helmetCondensationDropsChangedThisFrame) | ||||
|             { | ||||
|                 // animated roll-off | ||||
|                 helmetCondensationDrops = Mathf.Clamp(helmetCondensationDrops - Time.deltaTime / 6f, 0f, 1f); | ||||
|             } | ||||
|             helmetCondensationDropsChangedThisFrame = false; | ||||
|             if (helmetCondensationDrops > VibilityThreshold) | ||||
|             { | ||||
|                 // HelmetCondensationDrops | ||||
|                 Color color = hud.helmetCondensationMaterial.color; | ||||
|                 // set the final value to the greatest of the two, so that we don't accidentally undo steam's visual effect. | ||||
|                 color.a = Mathf.Clamp(Mathf.Max(color.a, helmetCondensationDrops), 0f, 0.27f); | ||||
|                 hud.helmetCondensationMaterial.color = color; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ClearCondensation(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static void Clear() | ||||
|         { | ||||
|             ClearDrunkness(); | ||||
|             ClearCondensation(); | ||||
|         } | ||||
| 
 | ||||
|         private static void ClearDrunkness() | ||||
|         { | ||||
|             drunkness = 0f; | ||||
|             // Only the stop animation if vanilla doesn't animate TZP right now. | ||||
|             if (GameNetworkManager.Instance.localPlayerController.drunkness == 0f) | ||||
|             { | ||||
|                 HUDManager.Instance.gasHelmetAnimator.SetBool("gasEmitting", value: false); | ||||
|             } | ||||
|             // Vanilla will set drunknessFilter.weight and gasImageAlpha.alpha on the next Update anyway. | ||||
|         } | ||||
| 
 | ||||
|         private static void ClearCondensation() | ||||
|         { | ||||
|             helmetCondensationDrops = 0f; | ||||
|         } | ||||
| 
 | ||||
|         [HarmonyPatch(typeof(HUDManager))] | ||||
|         internal static class HUDManagerScreenFiltersPatch | ||||
|         { | ||||
|             [HarmonyPatch(nameof(HUDManager.SetScreenFilters))] | ||||
|             [HarmonyPostfix] | ||||
|             static void SetScreenFiltersPostfix(HUDManager __instance) | ||||
|             { | ||||
|                 Update(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|     "name": "MuzikaGromche", | ||||
|     "version_number": "1337.420.9004", | ||||
|     "version_number": "1337.420.9003", | ||||
|     "author": "Ratijas", | ||||
|     "description": "Add some content to your inverse teleporter experience on Titan!", | ||||
|     "website_url": "https://git.vilunov.me/ratijas/muzika-gromche", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue