forked from nikita/muzika-gromche
Added new track AttentionPls, implement HUD effects as a time series / timeline
This commit is contained in:
parent
e67c72951e
commit
aea755361b
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,6 +3,7 @@
|
||||||
## MuzikaGromche 1337.420.9004
|
## MuzikaGromche 1337.420.9004
|
||||||
|
|
||||||
- Override Death Screen / Game Over text in certain cases.
|
- 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
|
## MuzikaGromche 1337.420.9003 - Lights Out Edition
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,9 @@
|
||||||
<Reference Include="UnityEngine.UI" Publicize="true" Private="False">
|
<Reference Include="UnityEngine.UI" Publicize="true" Private="False">
|
||||||
<HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\UnityEngine.UI.dll</HintPath>
|
<HintPath>$(LethalCompanyDir)Lethal Company_Data\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
</Reference>
|
</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>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
|
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
|
||||||
|
|
|
@ -13,6 +13,7 @@ using System.Net.NetworkInformation;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Networking;
|
using UnityEngine.Networking;
|
||||||
|
@ -56,6 +57,9 @@ namespace MuzikaGromche
|
||||||
ColorTransitionOut = 0.25f,
|
ColorTransitionOut = 0.25f,
|
||||||
ColorTransitionEasing = Easing.OutExpo,
|
ColorTransitionEasing = Easing.OutExpo,
|
||||||
FlickerLightsTimeSeries = [-5, 29, 61],
|
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"]),
|
Palette = Palette.Parse(["#B300FF", "#FFF100", "#00FF51", "#474747", "#FF00B3", "#0070FF"]),
|
||||||
Lyrics = [
|
Lyrics = [
|
||||||
(-68, "Devchata pljashut pod spidami"),
|
(-68, "Devchata pljashut pod spidami"),
|
||||||
|
@ -132,6 +136,9 @@ namespace MuzikaGromche
|
||||||
ColorTransitionOut = 0.25f,
|
ColorTransitionOut = 0.25f,
|
||||||
ColorTransitionEasing = Easing.OutExpo,
|
ColorTransitionEasing = Easing.OutExpo,
|
||||||
FlickerLightsTimeSeries = [-101, -93, -77, -61, -37, -5, 27],
|
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"]),
|
Palette = Palette.Parse(["#217F87", "#BAFF00", "#73BE25", "#78AB4E", "#FFFF00"]),
|
||||||
Lyrics = [
|
Lyrics = [
|
||||||
(-111, "Deploy Destroy, porjadok eto otstoj"),
|
(-111, "Deploy Destroy, porjadok eto otstoj"),
|
||||||
|
@ -583,6 +590,57 @@ namespace MuzikaGromche
|
||||||
FlickerLightsTimeSeries = [-68.5f, -16.5f, 30.5f],
|
FlickerLightsTimeSeries = [-68.5f, -16.5f, 30.5f],
|
||||||
Lyrics = [],
|
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()
|
public static ISelectableTrack ChooseTrack()
|
||||||
|
@ -686,6 +744,7 @@ namespace MuzikaGromche
|
||||||
harmony.PatchAll(typeof(DiscoBallDespawnPatch));
|
harmony.PatchAll(typeof(DiscoBallDespawnPatch));
|
||||||
harmony.PatchAll(typeof(SpawnRatePatch));
|
harmony.PatchAll(typeof(SpawnRatePatch));
|
||||||
harmony.PatchAll(typeof(DeathScreenGameOverTextResetPatch));
|
harmony.PatchAll(typeof(DeathScreenGameOverTextResetPatch));
|
||||||
|
harmony.PatchAll(typeof(ScreenFiltersManager.HUDManagerScreenFiltersPatch));
|
||||||
NetcodePatcher();
|
NetcodePatcher();
|
||||||
Compatibility.Register(this);
|
Compatibility.Register(this);
|
||||||
}
|
}
|
||||||
|
@ -795,6 +854,35 @@ 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
|
// An instance of a track which appears as a configuration entry and
|
||||||
// can be selected using weighted random from a list of selectable tracks.
|
// can be selected using weighted random from a list of selectable tracks.
|
||||||
public interface ISelectableTrack
|
public interface ISelectableTrack
|
||||||
|
@ -887,6 +975,9 @@ namespace MuzikaGromche
|
||||||
// If the chosen alternative is an empty string, lyrics event shall be skipped.
|
// If the chosen alternative is an empty string, lyrics event shall be skipped.
|
||||||
public string[] LyricsLines { get; }
|
public string[] LyricsLines { get; }
|
||||||
|
|
||||||
|
public TimeSeries<float> DrunknessLoopOffsetTimeSeries { get; }
|
||||||
|
public TimeSeries<float> CondensationLoopOffsetTimeSeries { get; }
|
||||||
|
|
||||||
public Palette Palette { get; }
|
public Palette Palette { get; }
|
||||||
|
|
||||||
public string? GameOverText { get => null; }
|
public string? GameOverText { get => null; }
|
||||||
|
@ -914,6 +1005,8 @@ namespace MuzikaGromche
|
||||||
float[] IAudioTrack.FlickerLightsTimeSeries => Track.FlickerLightsTimeSeries;
|
float[] IAudioTrack.FlickerLightsTimeSeries => Track.FlickerLightsTimeSeries;
|
||||||
float[] IAudioTrack.LyricsTimeSeries => Track.LyricsTimeSeries;
|
float[] IAudioTrack.LyricsTimeSeries => Track.LyricsTimeSeries;
|
||||||
string[] IAudioTrack.LyricsLines => Track.LyricsLines;
|
string[] IAudioTrack.LyricsLines => Track.LyricsLines;
|
||||||
|
TimeSeries<float> IAudioTrack.DrunknessLoopOffsetTimeSeries => Track.DrunknessLoopOffsetTimeSeries;
|
||||||
|
TimeSeries<float> IAudioTrack.CondensationLoopOffsetTimeSeries => Track.CondensationLoopOffsetTimeSeries;
|
||||||
Palette IAudioTrack.Palette => Track.Palette;
|
Palette IAudioTrack.Palette => Track.Palette;
|
||||||
string? IAudioTrack.GameOverText => Track.GameOverText;
|
string? IAudioTrack.GameOverText => Track.GameOverText;
|
||||||
}
|
}
|
||||||
|
@ -990,6 +1083,9 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimeSeries<float> DrunknessLoopOffsetTimeSeries { get; init; } = new();
|
||||||
|
public TimeSeries<float> CondensationLoopOffsetTimeSeries { get; init; } = new();
|
||||||
|
|
||||||
public Palette Palette { get; set; } = Palette.DEFAULT;
|
public Palette Palette { get; set; } = Palette.DEFAULT;
|
||||||
|
|
||||||
public string? GameOverText { get; init; } = null;
|
public string? GameOverText { get; init; } = null;
|
||||||
|
@ -1198,9 +1294,19 @@ namespace MuzikaGromche
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly float Duration()
|
public readonly float Duration(bool longest = false)
|
||||||
{
|
{
|
||||||
if (IsEmpty())
|
if (longest)
|
||||||
|
{
|
||||||
|
var to = BeatToInclusive;
|
||||||
|
if (BeatFromExclusive >= 0f && BeatToInclusive >= 0f && to < BeatFromExclusive)
|
||||||
|
{
|
||||||
|
// wrapped
|
||||||
|
to += LoopBeats;
|
||||||
|
}
|
||||||
|
return Mathf.Max(0f, to - BeatFromExclusive);
|
||||||
|
}
|
||||||
|
else if (IsEmpty())
|
||||||
{
|
{
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
@ -1455,8 +1561,8 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
if (AudioState.HasStarted)
|
if (AudioState.HasStarted)
|
||||||
{
|
{
|
||||||
var loopTimestamp = Update(LoopLoopingState);
|
var loopOffsetTimestamp = Update(LoopLoopingState);
|
||||||
var loopOffsetSpan = BeatTimeSpan.Between(LastKnownLoopOffsetBeat, loopTimestamp);
|
var loopOffsetSpan = BeatTimeSpan.Between(LastKnownLoopOffsetBeat, loopOffsetTimestamp);
|
||||||
|
|
||||||
// Do not go back in time
|
// Do not go back in time
|
||||||
if (!loopOffsetSpan.IsEmpty())
|
if (!loopOffsetSpan.IsEmpty())
|
||||||
|
@ -1467,8 +1573,8 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
|
|
||||||
var windUpOffsetTimestamp = Update(WindUpLoopingState);
|
var windUpOffsetTimestamp = Update(WindUpLoopingState);
|
||||||
LastKnownLoopOffsetBeat = loopTimestamp.Beat;
|
LastKnownLoopOffsetBeat = loopOffsetTimestamp.Beat;
|
||||||
var events = GetEvents(loopOffsetSpan, windUpOffsetTimestamp);
|
var events = GetEvents(loopOffsetTimestamp, loopOffsetSpan, windUpOffsetTimestamp);
|
||||||
#if DEBUG
|
#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)}");
|
Debug.Log($"{nameof(MuzikaGromche)} looping? {(LoopLoopingState.IsLooping ? 'X' : '_')}{(WindUpLoopingState.IsLooping ? 'X' : '_')} Loop={loopOffsetSpan} WindUp={windUpOffsetTimestamp} Time={Time.realtimeSinceStartup:N4} events={string.Join(",", events)}");
|
||||||
#endif
|
#endif
|
||||||
|
@ -1484,13 +1590,13 @@ namespace MuzikaGromche
|
||||||
return loopingState.Update(AudioState.Time, AudioState.IsExtrapolated, AdditionalOffset());
|
return loopingState.Update(AudioState.Time, AudioState.IsExtrapolated, AdditionalOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timings that may be changes through config
|
// Timings that may be changed through config
|
||||||
private float AdditionalOffset()
|
private float AdditionalOffset()
|
||||||
{
|
{
|
||||||
return Config.AudioOffset.Value + track.BeatsOffsetInSeconds;
|
return Config.AudioOffset.Value + track.BeatsOffsetInSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BaseEvent> GetEvents(BeatTimeSpan loopOffsetSpan, BeatTimestamp windUpOffsetTimestamp)
|
private List<BaseEvent> GetEvents(BeatTimestamp loopOffsetTimestamp, BeatTimeSpan loopOffsetSpan, BeatTimestamp windUpOffsetTimestamp)
|
||||||
{
|
{
|
||||||
List<BaseEvent> events = [];
|
List<BaseEvent> events = [];
|
||||||
|
|
||||||
|
@ -1511,7 +1617,6 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: quick editor
|
// TODO: quick editor
|
||||||
// loopOffsetSpan.GetLastIndex(Config.LyricsTimeSeries)
|
|
||||||
if (Config.DisplayLyrics.Value)
|
if (Config.DisplayLyrics.Value)
|
||||||
{
|
{
|
||||||
var index = loopOffsetSpan.GetLastIndex(track.LyricsTimeSeries);
|
var index = loopOffsetSpan.GetLastIndex(track.LyricsTimeSeries);
|
||||||
|
@ -1528,6 +1633,16 @@ 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;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1631,6 +1746,84 @@ 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;
|
abstract class BaseEvent;
|
||||||
|
@ -1706,6 +1899,20 @@ namespace MuzikaGromche
|
||||||
public override string ToString() => "WindUp";
|
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
|
// Default C#/.NET remainder operator % returns negative result for negative input
|
||||||
// which is unsuitable as an index for an array.
|
// which is unsuitable as an index for an array.
|
||||||
static class Mod
|
static class Mod
|
||||||
|
@ -1840,6 +2047,8 @@ namespace MuzikaGromche
|
||||||
private static string? ColorTransitionEasingOverride = null;
|
private static string? ColorTransitionEasingOverride = null;
|
||||||
private static float[]? FlickerLightsTimeSeriesOverride = null;
|
private static float[]? FlickerLightsTimeSeriesOverride = null;
|
||||||
private static float[]? LyricsTimeSeriesOverride = null;
|
private static float[]? LyricsTimeSeriesOverride = null;
|
||||||
|
private static TimeSeries<float>? DrunknessLoopOffsetTimeSeriesOverride = null;
|
||||||
|
private static TimeSeries<float>? CondensationLoopOffsetTimeSeriesOverride = null;
|
||||||
private static Palette? PaletteOverride = null;
|
private static Palette? PaletteOverride = null;
|
||||||
|
|
||||||
private class AudioTrackWithConfigOverride(IAudioTrack track) : ProxyAudioTrack(track), IAudioTrack
|
private class AudioTrackWithConfigOverride(IAudioTrack track) : ProxyAudioTrack(track), IAudioTrack
|
||||||
|
@ -1864,6 +2073,9 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
float[] IAudioTrack.LyricsTimeSeries => LyricsTimeSeriesOverride ?? Track.LyricsTimeSeries;
|
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;
|
Palette IAudioTrack.Palette => PaletteOverride ?? Track.Palette;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1888,11 +2100,12 @@ namespace MuzikaGromche
|
||||||
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(OverrideSpawnRates, Default(new BoolCheckBoxOptions())));
|
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(OverrideSpawnRates, Default(new BoolCheckBoxOptions())));
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
SetupEntriesForGameOverText(configFile);
|
||||||
|
SetupEntriesForScreenFilters(configFile);
|
||||||
SetupEntriesForExtrapolation(configFile);
|
SetupEntriesForExtrapolation(configFile);
|
||||||
SetupEntriesToSkipWinding(configFile);
|
SetupEntriesToSkipWinding(configFile);
|
||||||
SetupEntriesForPaletteOverride(configFile);
|
SetupEntriesForPaletteOverride(configFile);
|
||||||
SetupEntriesForTimingsOverride(configFile);
|
SetupEntriesForTimingsOverride(configFile);
|
||||||
SetupEntriesForGameOverText(configFile);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var chanceRange = new AcceptableValueRange<int>(0, 100);
|
var chanceRange = new AcceptableValueRange<int>(0, 100);
|
||||||
|
@ -2083,6 +2296,8 @@ namespace MuzikaGromche
|
||||||
ConfigEntry<float> fadeOutDurationEntry = null!;
|
ConfigEntry<float> fadeOutDurationEntry = null!;
|
||||||
ConfigEntry<string> flickerLightsTimeSeriesEntry = null!;
|
ConfigEntry<string> flickerLightsTimeSeriesEntry = null!;
|
||||||
ConfigEntry<string> lyricsTimeSeriesEntry = null!;
|
ConfigEntry<string> lyricsTimeSeriesEntry = null!;
|
||||||
|
ConfigEntry<string> drunknessTimeSeriesEntry = null!;
|
||||||
|
ConfigEntry<string> condensationTimeSeriesEntry = null!;
|
||||||
ConfigEntry<float> beatsOffsetEntry = null!;
|
ConfigEntry<float> beatsOffsetEntry = null!;
|
||||||
ConfigEntry<float> colorTransitionInEntry = null!;
|
ConfigEntry<float> colorTransitionInEntry = null!;
|
||||||
ConfigEntry<float> colorTransitionOutEntry = null!;
|
ConfigEntry<float> colorTransitionOutEntry = null!;
|
||||||
|
@ -2103,9 +2318,13 @@ namespace MuzikaGromche
|
||||||
fadeOutDurationEntry = configFile.Bind(section, "Fade Out Duration", 0f,
|
fadeOutDurationEntry = configFile.Bind(section, "Fade Out Duration", 0f,
|
||||||
new ConfigDescription("Duration of fading out", new AcceptableValueRange<float>(0, 10)));
|
new ConfigDescription("Duration of fading out", new AcceptableValueRange<float>(0, 10)));
|
||||||
flickerLightsTimeSeriesEntry = configFile.Bind(section, "Flicker Lights Time Series", "",
|
flickerLightsTimeSeriesEntry = configFile.Bind(section, "Flicker Lights Time Series", "",
|
||||||
new ConfigDescription("Time series of beat offsets when to flicker the lights."));
|
new ConfigDescription("Time series of loop offset beats when to flicker the lights."));
|
||||||
lyricsTimeSeriesEntry = configFile.Bind(section, "Lyrics Time Series", "",
|
lyricsTimeSeriesEntry = configFile.Bind(section, "Lyrics Time Series", "",
|
||||||
new ConfigDescription("Time series of beat offsets when to show lyrics lines."));
|
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"));
|
||||||
beatsOffsetEntry = configFile.Bind(section, "Beats Offset", 0f,
|
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)));
|
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,
|
colorTransitionInEntry = configFile.Bind(section, "Color Transition In", 0.25f,
|
||||||
|
@ -2120,6 +2339,8 @@ namespace MuzikaGromche
|
||||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(fadeOutDurationEntry, floatSliderOptions));
|
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(fadeOutDurationEntry, floatSliderOptions));
|
||||||
LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(flickerLightsTimeSeriesEntry, Default(new TextInputFieldOptions())));
|
LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(flickerLightsTimeSeriesEntry, Default(new TextInputFieldOptions())));
|
||||||
LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(lyricsTimeSeriesEntry, 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(beatsOffsetEntry, floatSliderOptions));
|
||||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionInEntry, floatSliderOptions));
|
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionInEntry, floatSliderOptions));
|
||||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionOutEntry, floatSliderOptions));
|
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionOutEntry, floatSliderOptions));
|
||||||
|
@ -2129,6 +2350,8 @@ namespace MuzikaGromche
|
||||||
registerStruct(fadeOutDurationEntry, t => t.FadeOutDuration, x => FadeOutDurationOverride = x);
|
registerStruct(fadeOutDurationEntry, t => t.FadeOutDuration, x => FadeOutDurationOverride = x);
|
||||||
registerArray(flickerLightsTimeSeriesEntry, t => t.FlickerLightsTimeSeries, xs => FlickerLightsTimeSeriesOverride = xs, float.Parse, sort: true);
|
registerArray(flickerLightsTimeSeriesEntry, t => t.FlickerLightsTimeSeries, xs => FlickerLightsTimeSeriesOverride = xs, float.Parse, sort: true);
|
||||||
registerArray(lyricsTimeSeriesEntry, t => t.LyricsTimeSeries, xs => LyricsTimeSeriesOverride = 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(beatsOffsetEntry, t => t.BeatsOffset, x => BeatsOffsetOverride = x);
|
||||||
registerStruct(colorTransitionInEntry, t => t.ColorTransitionIn, x => ColorTransitionInOverride = x);
|
registerStruct(colorTransitionInEntry, t => t.ColorTransitionIn, x => ColorTransitionInOverride = x);
|
||||||
registerStruct(colorTransitionOutEntry, t => t.ColorTransitionOut, x => ColorTransitionOutOverride = x);
|
registerStruct(colorTransitionOutEntry, t => t.ColorTransitionOut, x => ColorTransitionOutOverride = x);
|
||||||
|
@ -2162,7 +2385,76 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
setter.Invoke(overrideTimingsEntry.Value ? values : null);
|
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
|
T[]? parseStringArray<T>(string str, Func<string, T> parser, bool sort = false) where T : struct
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -2221,6 +2513,32 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
DeathScreenGameOverTextManager.Clear();
|
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
|
#endif
|
||||||
|
|
||||||
private T Default<T>(T options) where T : BaseOptions
|
private T Default<T>(T options) where T : BaseOptions
|
||||||
|
@ -2498,6 +2816,7 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
PoweredLightsBehaviour.Instance.ResetLightColor();
|
PoweredLightsBehaviour.Instance.ResetLightColor();
|
||||||
DiscoBallManager.Disable();
|
DiscoBallManager.Disable();
|
||||||
|
ScreenFiltersManager.Clear();
|
||||||
// Rotate track groups
|
// Rotate track groups
|
||||||
behaviour.ChooseTrackServerRpc();
|
behaviour.ChooseTrackServerRpc();
|
||||||
behaviour.BeatTimeState = null;
|
behaviour.BeatTimeState = null;
|
||||||
|
@ -2507,6 +2826,7 @@ namespace MuzikaGromche
|
||||||
else if ((__instance.previousState == 1 || __instance.previousState == 2) && behaviour.BeatTimeState is { } beatTimeState)
|
else if ((__instance.previousState == 1 || __instance.previousState == 2) && behaviour.BeatTimeState is { } beatTimeState)
|
||||||
{
|
{
|
||||||
var events = beatTimeState.Update(introAudioSource, loopAudioSource);
|
var events = beatTimeState.Update(introAudioSource, loopAudioSource);
|
||||||
|
var localPlayerCanHearMusic = Plugin.LocalPlayerCanHearMusic(__instance);
|
||||||
foreach (var ev in events)
|
foreach (var ev in events)
|
||||||
{
|
{
|
||||||
switch (ev)
|
switch (ev)
|
||||||
|
@ -2523,11 +2843,16 @@ namespace MuzikaGromche
|
||||||
RoundManager.Instance.FlickerLights(true);
|
RoundManager.Instance.FlickerLights(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LyricsEvent e:
|
case LyricsEvent e when localPlayerCanHearMusic:
|
||||||
if (Plugin.LocalPlayerCanHearMusic(__instance))
|
Plugin.DisplayLyrics(e.Text);
|
||||||
{
|
break;
|
||||||
Plugin.DisplayLyrics(e.Text);
|
|
||||||
}
|
case DrunkEvent e when localPlayerCanHearMusic:
|
||||||
|
ScreenFiltersManager.Drunkness = e.Drunkness;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CondensationEvent e when localPlayerCanHearMusic:
|
||||||
|
ScreenFiltersManager.HelmetCondensationDrops = e.Condensation;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2561,6 +2886,7 @@ namespace MuzikaGromche
|
||||||
PoweredLightsBehaviour.Instance.ResetLightColor();
|
PoweredLightsBehaviour.Instance.ResetLightColor();
|
||||||
DiscoBallManager.Disable();
|
DiscoBallManager.Disable();
|
||||||
DeathScreenGameOverTextManager.Clear();
|
DeathScreenGameOverTextManager.Clear();
|
||||||
|
ScreenFiltersManager.Clear();
|
||||||
// Just in case if players have spawned multiple Jesters,
|
// Just in case if players have spawned multiple Jesters,
|
||||||
// Don't reset Config.CurrentTrack to null,
|
// Don't reset Config.CurrentTrack to null,
|
||||||
// so that the latest chosen track remains set.
|
// so that the latest chosen track remains set.
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue