forked from nikita/muzika-gromche
Compare commits
No commits in common. "d02e594457e772f83b798a6ee84a32469cbdc398" and "c7b67b9042bb185d5ac6d8f7670673e4aed5c2d9" have entirely different histories.
d02e594457
...
c7b67b9042
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
## MuzikaGromche 13.37.9001
|
## MuzikaGromche 13.37.9001
|
||||||
|
|
||||||
- Fixed more missing flickering behaviours for some animators controllers.
|
|
||||||
- Fixed some powered lights not fully turning off or flickering when there are multiple Light components per container.
|
|
||||||
- Improved performance by pre-loading certain assets at the start of round instead of at a timing-critical frame update.
|
|
||||||
|
|
||||||
## MuzikaGromche 13.37.1337 - Photosensitivity Warning Edition
|
## MuzikaGromche 13.37.1337 - Photosensitivity Warning Edition
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using DunGen;
|
using DunGen;
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -9,21 +7,54 @@ using UnityEngine;
|
||||||
|
|
||||||
namespace MuzikaGromche
|
namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
public static class DiscoBallManager
|
public class DiscoBallManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
// A struct holding a disco ball container object and the name of a tile for which it was designed.
|
// A struct holding a disco ball container object and the name of a tile for which it was designed.
|
||||||
private readonly record struct TilePatch(string TileName, GameObject DiscoBallContainer)
|
public readonly record struct Data(string TileName, GameObject DiscoBallContainer)
|
||||||
{
|
{
|
||||||
// We are specifically looking for cloned tiles, not the original prototypes.
|
// We are specifically looking for cloned tiles, not the original prototypes.
|
||||||
public readonly string TileCloneName = $"{TileName}(Clone)";
|
public readonly string TileCloneName = $"{TileName}(Clone)";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TilePatch[] Patches = [];
|
public static readonly List<Data> Containers = [];
|
||||||
|
private static readonly List<GameObject> InstantiatedContainers = [];
|
||||||
|
|
||||||
private static readonly List<GameObject> CachedDiscoBalls = [];
|
public static void Initialize()
|
||||||
private static readonly List<Animator> CachedDiscoBallAnimators = [];
|
{
|
||||||
|
string bundlePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "muzikagromche_discoball");
|
||||||
|
var bundle = AssetBundle.LoadFromFile(bundlePath);
|
||||||
|
|
||||||
private static readonly string[] AnimatorContainersNames = [
|
foreach ((string prefabPath, string tileName) in new[] {
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerManor.prefab", "ManorStartRoomSmall"),
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerManorOLD.prefab", "ManorStartRoom"),
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerFactory.prefab", "StartRoom"),
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerMineShaft.prefab", "MineshaftStartTile"),
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerLargeForkTileB.prefab", "LargeForkTileB"),
|
||||||
|
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerBirthdayRoomTile.prefab", "BirthdayRoomTile"),
|
||||||
|
})
|
||||||
|
{
|
||||||
|
var container = bundle.LoadAsset<GameObject>(prefabPath);
|
||||||
|
Containers.Add(new(tileName, container));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
|
{
|
||||||
|
// Just in case
|
||||||
|
Disable();
|
||||||
|
|
||||||
|
var query = from tile in Resources.FindObjectsOfTypeAll<Tile>()
|
||||||
|
join container in Containers
|
||||||
|
on tile.gameObject.name equals container.TileCloneName
|
||||||
|
select (tile, container);
|
||||||
|
|
||||||
|
foreach (var (tile, container) in query)
|
||||||
|
{
|
||||||
|
Enable(tile, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly string[] animatorNames = [
|
||||||
"DiscoBallProp/AnimContainer",
|
"DiscoBallProp/AnimContainer",
|
||||||
"DiscoBallProp1/AnimContainer",
|
"DiscoBallProp1/AnimContainer",
|
||||||
"DiscoBallProp2/AnimContainer",
|
"DiscoBallProp2/AnimContainer",
|
||||||
|
@ -32,134 +63,29 @@ namespace MuzikaGromche
|
||||||
"DiscoBallProp5/AnimContainer",
|
"DiscoBallProp5/AnimContainer",
|
||||||
];
|
];
|
||||||
|
|
||||||
public static void Load()
|
private static void Enable(Tile tile, Data container)
|
||||||
{
|
{
|
||||||
const string BundleFileName = "muzikagromche_discoball";
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Enabling at '{tile.gameObject.name}'");
|
||||||
string bundlePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), BundleFileName);
|
var discoBall = Instantiate(container.DiscoBallContainer, tile.transform);
|
||||||
var assetBundle = AssetBundle.LoadFromFile(bundlePath)
|
InstantiatedContainers.Add(discoBall);
|
||||||
?? throw new NullReferenceException("Failed to load bundle");
|
|
||||||
|
|
||||||
(string PrefabPath, string TileName)[] patchDescriptors =
|
foreach (var animatorName in animatorNames)
|
||||||
[
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerManor.prefab", "ManorStartRoomSmall"),
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerManorOLD.prefab", "ManorStartRoom"),
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerFactory.prefab", "StartRoom"),
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerMineShaft.prefab", "MineshaftStartTile"),
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerLargeForkTileB.prefab", "LargeForkTileB"),
|
|
||||||
("Assets/LethalCompany/Mods/MuzikaGromche/DiscoBallContainerBirthdayRoomTile.prefab", "BirthdayRoomTile"),
|
|
||||||
];
|
|
||||||
|
|
||||||
Patches = [.. patchDescriptors.Select(d =>
|
|
||||||
new TilePatch(d.TileName, assetBundle.LoadAsset<GameObject>(d.PrefabPath))
|
|
||||||
)];
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Patch(Tile tile)
|
|
||||||
{
|
|
||||||
var query = from patch in Patches
|
|
||||||
where tile.gameObject.name == patch.TileCloneName
|
|
||||||
select patch;
|
|
||||||
|
|
||||||
// Should be just one, but FirstOrDefault() isn't usable with structs
|
|
||||||
foreach (var patch in query)
|
|
||||||
{
|
{
|
||||||
Patch(tile, patch);
|
if (discoBall.transform.Find(animatorName)?.gameObject is GameObject animator)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Patch(Tile tile, TilePatch patch)
|
|
||||||
{
|
|
||||||
var discoBall = UnityEngine.Object.Instantiate(patch.DiscoBallContainer, tile.transform);
|
|
||||||
if (discoBall == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var animator in FindDiscoBallAnimators(discoBall))
|
|
||||||
{
|
|
||||||
CachedDiscoBallAnimators.Add(animator);
|
|
||||||
}
|
|
||||||
CachedDiscoBalls.Add(discoBall);
|
|
||||||
discoBall.SetActive(false);
|
|
||||||
|
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Patched tile '{tile.gameObject.name}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
static IEnumerable<Animator> FindDiscoBallAnimators(GameObject discoBall)
|
|
||||||
{
|
|
||||||
foreach (var animatorContainerName in AnimatorContainersNames)
|
|
||||||
{
|
|
||||||
var transform = discoBall.transform.Find(animatorContainerName);
|
|
||||||
if (transform == null)
|
|
||||||
{
|
{
|
||||||
// Not all prefabs have all possible animators, and it's OK
|
animator.GetComponent<Animator>().SetBool("on", true);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var animator = transform.gameObject?.GetComponent<Animator>();
|
|
||||||
if (animator == null)
|
|
||||||
{
|
|
||||||
// This would be weird
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return animator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Toggle(bool on)
|
|
||||||
{
|
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Toggle {(on ? "ON" : "OFF")} {CachedDiscoBallAnimators.Count} animators");
|
|
||||||
|
|
||||||
foreach (var discoBall in CachedDiscoBalls)
|
|
||||||
{
|
|
||||||
discoBall.SetActive(true);
|
|
||||||
}
|
|
||||||
foreach (var animator in CachedDiscoBallAnimators)
|
|
||||||
{
|
|
||||||
animator?.SetBool("on", on);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Enable()
|
|
||||||
{
|
|
||||||
Toggle(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Disable()
|
public static void Disable()
|
||||||
{
|
{
|
||||||
Toggle(false);
|
foreach (var discoBall in InstantiatedContainers)
|
||||||
}
|
{
|
||||||
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)}: Disabling {discoBall.name}");
|
||||||
internal static void Clear()
|
Destroy(discoBall);
|
||||||
{
|
}
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Clearing {CachedDiscoBalls.Count} disco balls & {CachedDiscoBallAnimators.Count} animators");
|
InstantiatedContainers.Clear();
|
||||||
CachedDiscoBallAnimators.Clear();
|
|
||||||
CachedDiscoBalls.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Tile))]
|
|
||||||
static class DiscoBallTilePatch
|
|
||||||
{
|
|
||||||
[HarmonyPatch(nameof(Tile.AddTriggerVolume))]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
static void OnAddTriggerVolume(Tile __instance)
|
|
||||||
{
|
|
||||||
DiscoBallManager.Patch(__instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(RoundManager))]
|
|
||||||
static class DiscoBallDespawnPatch
|
|
||||||
{
|
|
||||||
[HarmonyPatch(nameof(RoundManager.DespawnPropsAtEndOfRound))]
|
|
||||||
[HarmonyPatch(nameof(RoundManager.OnDestroy))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
static void OnDestroy(RoundManager __instance)
|
|
||||||
{
|
|
||||||
var _ = __instance;
|
|
||||||
DiscoBallManager.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace MuzikaGromche
|
||||||
.Select(a => $" Trying... {a}")
|
.Select(a => $" Trying... {a}")
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Track[] Tracks = [
|
public static Track[] Tracks = [
|
||||||
new Track
|
new Track
|
||||||
{
|
{
|
||||||
Name = "MuzikaGromche",
|
Name = "MuzikaGromche",
|
||||||
|
@ -482,8 +482,8 @@ namespace MuzikaGromche
|
||||||
return tracks[trackId];
|
return tracks[trackId];
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Track? CurrentTrack;
|
public static Track? CurrentTrack;
|
||||||
internal static BeatTimeState? BeatTimeState;
|
public static BeatTimeState? BeatTimeState;
|
||||||
|
|
||||||
public static void SetLightColor(Color color)
|
public static void SetLightColor(Color color)
|
||||||
{
|
{
|
||||||
|
@ -513,14 +513,7 @@ namespace MuzikaGromche
|
||||||
return distance <= AudioMaxDistance;
|
return distance <= AudioMaxDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisplayLyrics(string text)
|
private void Awake()
|
||||||
{
|
|
||||||
HUDManager.Instance.DisplayTip("[Lyrics]", text);
|
|
||||||
// Don't interrupt the music with constant HUD audio pings
|
|
||||||
HUDManager.Instance.UIAudio.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Awake()
|
|
||||||
{
|
{
|
||||||
string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
UnityWebRequest[] requests = new UnityWebRequest[Tracks.Length * 2];
|
UnityWebRequest[] requests = new UnityWebRequest[Tracks.Length * 2];
|
||||||
|
@ -544,16 +537,12 @@ namespace MuzikaGromche
|
||||||
track.LoadedLoop = DownloadHandlerAudioClip.GetContent(requests[i * 2 + 1]);
|
track.LoadedLoop = DownloadHandlerAudioClip.GetContent(requests[i * 2 + 1]);
|
||||||
}
|
}
|
||||||
Config = new Config(base.Config);
|
Config = new Config(base.Config);
|
||||||
DiscoBallManager.Load();
|
DiscoBallManager.Initialize();
|
||||||
PoweredLightsAnimators.Load();
|
PoweredLightsAnimators.Load();
|
||||||
var harmony = new Harmony(PluginInfo.PLUGIN_NAME);
|
var harmony = new Harmony(PluginInfo.PLUGIN_NAME);
|
||||||
harmony.PatchAll(typeof(JesterPatch));
|
harmony.PatchAll(typeof(JesterPatch));
|
||||||
harmony.PatchAll(typeof(EnemyAIPatch));
|
harmony.PatchAll(typeof(EnemyAIPatch));
|
||||||
harmony.PatchAll(typeof(PoweredLightsAnimatorsPatch));
|
harmony.PatchAll(typeof(PoweredLightsAnimatorsPatch));
|
||||||
harmony.PatchAll(typeof(AllPoweredLightsPatch));
|
|
||||||
harmony.PatchAll(typeof(DiscoBallTilePatch));
|
|
||||||
harmony.PatchAll(typeof(DiscoBallDespawnPatch));
|
|
||||||
harmony.PatchAll(typeof(SpawnRatePatch));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -563,7 +552,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public readonly record struct Language(string Short, string Full)
|
public record Language(string Short, string Full)
|
||||||
{
|
{
|
||||||
public static readonly Language ENGLISH = new("EN", "English");
|
public static readonly Language ENGLISH = new("EN", "English");
|
||||||
public static readonly Language RUSSIAN = new("RU", "Russian");
|
public static readonly Language RUSSIAN = new("RU", "Russian");
|
||||||
|
@ -588,9 +577,9 @@ namespace MuzikaGromche
|
||||||
? Mathf.Pow(2f, 20f * x - 10f) / 2f
|
? Mathf.Pow(2f, 20f * x - 10f) / 2f
|
||||||
: (2f - Mathf.Pow(2f, -20f * x + 10f)) / 2f);
|
: (2f - Mathf.Pow(2f, -20f * x + 10f)) / 2f);
|
||||||
|
|
||||||
public static readonly Easing[] All = [Linear, InCubic, OutCubic, InOutCubic, InExpo, OutExpo, InOutExpo];
|
public static Easing[] All = [Linear, InCubic, OutCubic, InOutCubic, InExpo, OutExpo, InOutExpo];
|
||||||
|
|
||||||
public static readonly string[] AllNames = [.. All.Select(easing => easing.Name)];
|
public static string[] AllNames => [.. All.Select(easing => easing.Name)];
|
||||||
|
|
||||||
public static Easing FindByName(string Name)
|
public static Easing FindByName(string Name)
|
||||||
{
|
{
|
||||||
|
@ -603,9 +592,9 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly record struct Palette(Color[] Colors)
|
public record Palette(Color[] Colors)
|
||||||
{
|
{
|
||||||
public static readonly Palette DEFAULT = new([Color.magenta, Color.cyan, Color.green, Color.yellow]);
|
public static Palette DEFAULT = new([Color.magenta, Color.cyan, Color.green, Color.yellow]);
|
||||||
|
|
||||||
public static Palette Parse(string[] hexColors)
|
public static Palette Parse(string[] hexColors)
|
||||||
{
|
{
|
||||||
|
@ -787,7 +776,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly record struct BeatTimestamp
|
public readonly record struct BeatTimestamp
|
||||||
{
|
{
|
||||||
// Number of beats in the loop audio segment.
|
// Number of beats in the loop audio segment.
|
||||||
public readonly int LoopBeats;
|
public readonly int LoopBeats;
|
||||||
|
@ -837,7 +826,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly record struct BeatTimeSpan
|
public readonly record struct BeatTimeSpan
|
||||||
{
|
{
|
||||||
public readonly int LoopBeats;
|
public readonly int LoopBeats;
|
||||||
public readonly float HalfLoopBeats => LoopBeats / 2f;
|
public readonly float HalfLoopBeats => LoopBeats / 2f;
|
||||||
|
@ -987,7 +976,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BeatTimeState
|
public class BeatTimeState
|
||||||
{
|
{
|
||||||
// The object is newly created, the Start audio began to play but its time hasn't adjanced from 0.0f yet.
|
// The object is newly created, the Start audio began to play but its time hasn't adjanced from 0.0f yet.
|
||||||
private bool hasStarted = false;
|
private bool hasStarted = false;
|
||||||
|
@ -1254,9 +1243,9 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BaseEvent;
|
public abstract class BaseEvent;
|
||||||
|
|
||||||
class SetLightsColorEvent(Color color) : BaseEvent
|
public class SetLightsColorEvent(Color color) : BaseEvent
|
||||||
{
|
{
|
||||||
public readonly Color Color = color;
|
public readonly Color Color = color;
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -1265,7 +1254,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SetLightsColorTransitionEvent(Color from, Color to, Easing easing, float t)
|
public class SetLightsColorTransitionEvent(Color from, Color to, Easing easing, float t)
|
||||||
: SetLightsColorEvent(Color.Lerp(from, to, Mathf.Clamp(easing.Eval(t), 0f, 1f)))
|
: SetLightsColorEvent(Color.Lerp(from, to, Mathf.Clamp(easing.Eval(t), 0f, 1f)))
|
||||||
{
|
{
|
||||||
// Additional context for debugging
|
// Additional context for debugging
|
||||||
|
@ -1279,12 +1268,12 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlickerLightsEvent : BaseEvent
|
public class FlickerLightsEvent : BaseEvent
|
||||||
{
|
{
|
||||||
public override string ToString() => "Flicker";
|
public override string ToString() => "Flicker";
|
||||||
}
|
}
|
||||||
|
|
||||||
class LyricsEvent(string text) : BaseEvent
|
public class LyricsEvent(string text) : BaseEvent
|
||||||
{
|
{
|
||||||
public readonly string Text = text;
|
public readonly string Text = text;
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -1293,14 +1282,14 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WindUpZeroBeatEvent : BaseEvent
|
public class WindUpZeroBeatEvent : BaseEvent
|
||||||
{
|
{
|
||||||
public override string ToString() => "WindUp";
|
public override string ToString() => "WindUp";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
public static class Mod
|
||||||
{
|
{
|
||||||
public static int Positive(int x, int m)
|
public static int Positive(int x, int m)
|
||||||
{
|
{
|
||||||
|
@ -1320,7 +1309,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly struct RandomWeightedIndex
|
public readonly struct RandomWeightedIndex
|
||||||
{
|
{
|
||||||
public RandomWeightedIndex(int[] weights)
|
public RandomWeightedIndex(int[] weights)
|
||||||
{
|
{
|
||||||
|
@ -1407,7 +1396,7 @@ namespace MuzikaGromche
|
||||||
readonly public int TotalWeights { get; }
|
readonly public int TotalWeights { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
static class SyncedEntryExtensions
|
public static class SyncedEntryExtensions
|
||||||
{
|
{
|
||||||
// Update local values on clients. Even though the clients couldn't
|
// Update local values on clients. Even though the clients couldn't
|
||||||
// edit them, they could at least see the new values.
|
// edit them, they could at least see the new values.
|
||||||
|
@ -1420,7 +1409,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Config : SyncedConfig2<Config>
|
public class Config : SyncedConfig2<Config>
|
||||||
{
|
{
|
||||||
public static ConfigEntry<bool> DisplayLyrics { get; private set; } = null!;
|
public static ConfigEntry<bool> DisplayLyrics { get; private set; } = null!;
|
||||||
|
|
||||||
|
@ -1428,8 +1417,6 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
public static ConfigEntry<bool> SkipExplicitTracks { get; private set; } = null!;
|
public static ConfigEntry<bool> SkipExplicitTracks { get; private set; } = null!;
|
||||||
|
|
||||||
public static SyncedEntry<bool> OverrideSpawnRates { get; private set; } = null!;
|
|
||||||
|
|
||||||
public static bool ShouldSkipWindingPhase { get; private set; } = false;
|
public static bool ShouldSkipWindingPhase { get; private set; } = false;
|
||||||
|
|
||||||
public static Palette? PaletteOverride { get; private set; } = null;
|
public static Palette? PaletteOverride { get; private set; } = null;
|
||||||
|
@ -1443,7 +1430,7 @@ namespace MuzikaGromche
|
||||||
public static float? ColorTransitionOutOverride { get; private set; } = null;
|
public static float? ColorTransitionOutOverride { get; private set; } = null;
|
||||||
public static string? ColorTransitionEasingOverride { get; private set; } = null;
|
public static string? ColorTransitionEasingOverride { get; private set; } = null;
|
||||||
|
|
||||||
internal Config(ConfigFile configFile) : base(PluginInfo.PLUGIN_GUID)
|
public Config(ConfigFile configFile) : base(PluginInfo.PLUGIN_GUID)
|
||||||
{
|
{
|
||||||
DisplayLyrics = configFile.Bind("General", "Display Lyrics", true,
|
DisplayLyrics = configFile.Bind("General", "Display Lyrics", true,
|
||||||
new ConfigDescription("Display lyrics in the HUD tooltip when you hear the music."));
|
new ConfigDescription("Display lyrics in the HUD tooltip when you hear the music."));
|
||||||
|
@ -1458,11 +1445,6 @@ namespace MuzikaGromche
|
||||||
new ConfigDescription("When choosing tracks at random, skip the ones with Explicit Content/Lyrics."));
|
new ConfigDescription("When choosing tracks at random, skip the ones with Explicit Content/Lyrics."));
|
||||||
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(SkipExplicitTracks, requiresRestart: false));
|
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(SkipExplicitTracks, requiresRestart: false));
|
||||||
|
|
||||||
OverrideSpawnRates = configFile.BindSyncedEntry("General", "Override Spawn Rates", false,
|
|
||||||
new ConfigDescription("Deviate from vanilla spawn rates to experience content of this mod more often."));
|
|
||||||
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(OverrideSpawnRates.Entry, Default(new BoolCheckBoxOptions())));
|
|
||||||
CSyncHackAddSyncedEntry(OverrideSpawnRates);
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
SetupEntriesToSkipWinding(configFile);
|
SetupEntriesToSkipWinding(configFile);
|
||||||
SetupEntriesForPaletteOverride(configFile);
|
SetupEntriesForPaletteOverride(configFile);
|
||||||
|
@ -1777,26 +1759,19 @@ namespace MuzikaGromche
|
||||||
// farAudio is during windup, Start overrides popGoesTheWeaselTheme
|
// farAudio is during windup, Start overrides popGoesTheWeaselTheme
|
||||||
// creatureVoice is when popped, Loop overrides screamingSFX
|
// creatureVoice is when popped, Loop overrides screamingSFX
|
||||||
[HarmonyPatch(typeof(JesterAI))]
|
[HarmonyPatch(typeof(JesterAI))]
|
||||||
static class JesterPatch
|
internal class JesterPatch
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
[HarmonyPatch(nameof(JesterAI.SetJesterInitialValues))]
|
[HarmonyPatch(nameof(JesterAI.SetJesterInitialValues))]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
static void AlmostInstantFollowTimerPostfix(JesterAI __instance)
|
public static void AlmostInstantFollowTimerPostfix(JesterAI __instance)
|
||||||
{
|
{
|
||||||
__instance.beginCrankingTimer = 1f;
|
__instance.beginCrankingTimer = 1f;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class State
|
|
||||||
{
|
|
||||||
public required AudioSource farAudio;
|
|
||||||
public required int previousState;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(nameof(JesterAI.Update))]
|
[HarmonyPatch(nameof(JesterAI.Update))]
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
static void JesterUpdatePrefix(JesterAI __instance, out State __state)
|
public static void DoNotStopTheMusicPrefix(JesterAI __instance, out State __state)
|
||||||
{
|
{
|
||||||
__state = new State
|
__state = new State
|
||||||
{
|
{
|
||||||
|
@ -1818,7 +1793,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
[HarmonyPatch(nameof(JesterAI.Update))]
|
[HarmonyPatch(nameof(JesterAI.Update))]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
static void JesterUpdatePostfix(JesterAI __instance, State __state)
|
public static void DoNotStopTheMusic(JesterAI __instance, State __state)
|
||||||
{
|
{
|
||||||
if (__instance.previousState == 1 && __state.previousState != 1)
|
if (__instance.previousState == 1 && __state.previousState != 1)
|
||||||
{
|
{
|
||||||
|
@ -1900,7 +1875,9 @@ namespace MuzikaGromche
|
||||||
case LyricsEvent e:
|
case LyricsEvent e:
|
||||||
if (Plugin.LocalPlayerCanHearMusic(__instance))
|
if (Plugin.LocalPlayerCanHearMusic(__instance))
|
||||||
{
|
{
|
||||||
Plugin.DisplayLyrics(e.Text);
|
HUDManager.Instance.DisplayTip("[Lyrics]", e.Text);
|
||||||
|
// Don't interrupt the music with constant HUD audio pings
|
||||||
|
HUDManager.Instance.UIAudio.Stop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1910,13 +1887,13 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch(typeof(EnemyAI))]
|
[HarmonyPatch(typeof(EnemyAI))]
|
||||||
static class EnemyAIPatch
|
internal class EnemyAIPatch
|
||||||
{
|
{
|
||||||
// JesterAI class does not override abstract method OnDestroy,
|
// JesterAI class does not override abstract method OnDestroy,
|
||||||
// so we have to patch its superclass directly.
|
// so we have to patch its superclass directly.
|
||||||
[HarmonyPatch(nameof(EnemyAI.OnDestroy))]
|
[HarmonyPatch(nameof(EnemyAI.OnDestroy))]
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
static void CleanUpOnDestroy(EnemyAI __instance)
|
public static void CleanUpOnDestroy(EnemyAI __instance)
|
||||||
{
|
{
|
||||||
if (__instance is JesterAI)
|
if (__instance is JesterAI)
|
||||||
{
|
{
|
||||||
|
@ -1928,4 +1905,10 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class State
|
||||||
|
{
|
||||||
|
public required AudioSource farAudio;
|
||||||
|
public required int previousState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,9 @@ using UnityEngine;
|
||||||
|
|
||||||
namespace MuzikaGromche
|
namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
static class PoweredLightsAnimators
|
internal class PoweredLightsAnimators
|
||||||
{
|
{
|
||||||
private const string PoweredLightTag = "PoweredLight";
|
private readonly record struct AnimatorPatch(string AnimatorContainerPath, RuntimeAnimatorController AnimatorController);
|
||||||
|
|
||||||
private delegate void ManualPatch(GameObject animatorContainer);
|
|
||||||
|
|
||||||
private readonly record struct AnimatorPatch(
|
|
||||||
string AnimatorContainerPath,
|
|
||||||
RuntimeAnimatorController AnimatorController,
|
|
||||||
bool AddTagAndAnimator,
|
|
||||||
ManualPatch? ManualPatch);
|
|
||||||
|
|
||||||
private readonly record struct TilePatch(string TileName, AnimatorPatch[] Patches)
|
private readonly record struct TilePatch(string TileName, AnimatorPatch[] Patches)
|
||||||
{
|
{
|
||||||
|
@ -27,17 +19,13 @@ namespace MuzikaGromche
|
||||||
public readonly string TileCloneName = $"{TileName}(Clone)";
|
public readonly string TileCloneName = $"{TileName}(Clone)";
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly record struct AnimatorPatchDescriptor(
|
private readonly record struct AnimatorPatchDescriptor(string AnimatorContainerPath, string AnimatorControllerAssetPath)
|
||||||
string AnimatorContainerPath,
|
|
||||||
string AnimatorControllerAssetPath,
|
|
||||||
bool AddTagAndAnimator = false,
|
|
||||||
ManualPatch? ManualPatch = null)
|
|
||||||
{
|
{
|
||||||
public AnimatorPatch Load(AssetBundle assetBundle)
|
public AnimatorPatch Load(AssetBundle assetBundle)
|
||||||
{
|
{
|
||||||
var animationController = assetBundle.LoadAsset<RuntimeAnimatorController>(AnimatorControllerAssetPath)
|
var animationController = assetBundle.LoadAsset<RuntimeAnimatorController>(AnimatorControllerAssetPath)
|
||||||
?? throw new FileNotFoundException($"RuntimeAnimatorController not found: {AnimatorControllerAssetPath}", AnimatorControllerAssetPath);
|
?? throw new FileNotFoundException($"RuntimeAnimatorController not found: {AnimatorControllerAssetPath}", AnimatorControllerAssetPath);
|
||||||
return new(AnimatorContainerPath, animationController, AddTagAndAnimator, ManualPatch);
|
return new(AnimatorContainerPath, animationController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +45,6 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
private static IDictionary<string, TilePatch> Patches = new Dictionary<string, TilePatch>();
|
private static IDictionary<string, TilePatch> Patches = new Dictionary<string, TilePatch>();
|
||||||
|
|
||||||
private static AudioClip AudioClipOn = null!;
|
|
||||||
private static AudioClip AudioClipOff = null!;
|
|
||||||
private static AudioClip AudioClipFlicker = null!;
|
|
||||||
|
|
||||||
public static void Load()
|
public static void Load()
|
||||||
{
|
{
|
||||||
const string BundleFileName = "muzikagromche_poweredlightsanimators";
|
const string BundleFileName = "muzikagromche_poweredlightsanimators";
|
||||||
|
@ -68,26 +52,16 @@ namespace MuzikaGromche
|
||||||
var assetBundle = AssetBundle.LoadFromFile(bundlePath)
|
var assetBundle = AssetBundle.LoadFromFile(bundlePath)
|
||||||
?? throw new NullReferenceException("Failed to load bundle");
|
?? throw new NullReferenceException("Failed to load bundle");
|
||||||
|
|
||||||
AudioClipOn = assetBundle.LoadAsset<AudioClip>("Assets/LethalCompany/Mods/MuzikaGromche/AudioClips/LightOn.ogg");
|
|
||||||
AudioClipOff = assetBundle.LoadAsset<AudioClip>("Assets/LethalCompany/Mods/MuzikaGromche/AudioClips/LightOff.ogg");
|
|
||||||
AudioClipFlicker = assetBundle.LoadAsset<AudioClip>("Assets/LethalCompany/Mods/MuzikaGromche/AudioClips/LightFlicker.ogg");
|
|
||||||
|
|
||||||
const string BasePath = "Assets/LethalCompany/Mods/MuzikaGromche/AnimatorControllers/";
|
const string BasePath = "Assets/LethalCompany/Mods/MuzikaGromche/AnimatorControllers/";
|
||||||
|
|
||||||
const string PointLight4 = $"{BasePath}Point Light (4) (Patched).controller";
|
const string PointLight4 = $"{BasePath}Point Light (4) (Patched).controller";
|
||||||
const string MineshaftSpotlight = $"{BasePath}MineshaftSpotlight (Patched).controller";
|
const string MineshaftSpotlight = $"{BasePath}MineshaftSpotlight (Patched).controller";
|
||||||
const string LightbulbsLine = $"{BasePath}lightbulbsLineMesh (Patched).controller";
|
const string LightbulbsLine = $"{BasePath}lightbulbsLineMesh (Patched).controller";
|
||||||
const string CeilingFan = $"{BasePath}CeilingFan (originally GameObject) (Patched).controller";
|
const string CeilingFan = $"{BasePath}CeilingFan (originally GameObject) (Patched).controller";
|
||||||
const string LEDHangingLight = $"{BasePath}LEDHangingLight (Patched).controller";
|
|
||||||
const string MineshaftStartTileSpotlight = $"{BasePath}MineshaftStartTileSpotlight (New).controller";
|
|
||||||
|
|
||||||
TilePatchDescriptor[] descriptors =
|
TilePatchDescriptor[] descriptors =
|
||||||
[
|
[
|
||||||
// any version
|
|
||||||
new("KitchenTile", [
|
|
||||||
new("PoweredLightTypeB", PointLight4),
|
|
||||||
new("PoweredLightTypeB (1)", PointLight4),
|
|
||||||
]),
|
|
||||||
// < v70
|
// < v70
|
||||||
new("ManorStartRoom", [
|
new("ManorStartRoom", [
|
||||||
new("ManorStartRoom/Chandelier/PoweredLightTypeB (1)", PointLight4),
|
new("ManorStartRoom/Chandelier/PoweredLightTypeB (1)", PointLight4),
|
||||||
|
@ -98,10 +72,6 @@ namespace MuzikaGromche
|
||||||
new("ManorStartRoomMesh/Chandelier/PoweredLightTypeB (1)", PointLight4),
|
new("ManorStartRoomMesh/Chandelier/PoweredLightTypeB (1)", PointLight4),
|
||||||
new("ManorStartRoomMesh/Chandelier2/PoweredLightTypeB", PointLight4),
|
new("ManorStartRoomMesh/Chandelier2/PoweredLightTypeB", PointLight4),
|
||||||
]),
|
]),
|
||||||
new("NarrowHallwayTile2x2", [
|
|
||||||
new("MineshaftSpotlight (1)", MineshaftSpotlight),
|
|
||||||
new("MineshaftSpotlight (2)", MineshaftSpotlight),
|
|
||||||
]),
|
|
||||||
new("BirthdayRoomTile", [
|
new("BirthdayRoomTile", [
|
||||||
new("Lights/MineshaftSpotlight", MineshaftSpotlight),
|
new("Lights/MineshaftSpotlight", MineshaftSpotlight),
|
||||||
]),
|
]),
|
||||||
|
@ -113,21 +83,6 @@ namespace MuzikaGromche
|
||||||
new("CeilingFanAnimContainer", CeilingFan),
|
new("CeilingFanAnimContainer", CeilingFan),
|
||||||
new("MineshaftSpotlight (1)", MineshaftSpotlight),
|
new("MineshaftSpotlight (1)", MineshaftSpotlight),
|
||||||
]),
|
]),
|
||||||
new("GarageTile", [
|
|
||||||
new("HangingLEDBarLight (3)", LEDHangingLight),
|
|
||||||
new("HangingLEDBarLight (4)", LEDHangingLight,
|
|
||||||
// This HangingLEDBarLight's IndirectLight is wrongly named, so animator couldn't find it
|
|
||||||
ManualPatch: RenameGameObjectPatch("IndirectLight (1)", "IndirectLight")),
|
|
||||||
]),
|
|
||||||
new("PoolTile", [
|
|
||||||
new("PoolLights/HangingLEDBarLight", LEDHangingLight),
|
|
||||||
new("PoolLights/HangingLEDBarLight (4)", LEDHangingLight),
|
|
||||||
new("PoolLights/HangingLEDBarLight (5)", LEDHangingLight),
|
|
||||||
]),
|
|
||||||
new("MineshaftStartTile", [
|
|
||||||
new("Cylinder.001 (1)", MineshaftStartTileSpotlight, AddTagAndAnimator: true),
|
|
||||||
new("Cylinder.001 (2)", MineshaftStartTileSpotlight, AddTagAndAnimator: true),
|
|
||||||
]),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Patches = descriptors
|
Patches = descriptors
|
||||||
|
@ -157,41 +112,7 @@ namespace MuzikaGromche
|
||||||
#pragma warning restore CS0162 // Unreachable code detected
|
#pragma warning restore CS0162 // Unreachable code detected
|
||||||
}
|
}
|
||||||
|
|
||||||
GameObject animationContainer = animationContainerTransform.gameObject;
|
var animator = animationContainerTransform.gameObject.GetComponent<Animator>();
|
||||||
Animator animator = animationContainer.GetComponent<Animator>();
|
|
||||||
|
|
||||||
if (patch.AddTagAndAnimator)
|
|
||||||
{
|
|
||||||
animationContainer.tag = PoweredLightTag;
|
|
||||||
if (animator == null)
|
|
||||||
{
|
|
||||||
animator = animationContainer.AddComponent<Animator>();
|
|
||||||
}
|
|
||||||
if (!animationContainer.TryGetComponent<PlayAudioAnimationEvent>(out var audioScript))
|
|
||||||
{
|
|
||||||
audioScript = animationContainer.AddComponent<PlayAudioAnimationEvent>();
|
|
||||||
audioScript.audioClip = AudioClipOn;
|
|
||||||
audioScript.audioClip2 = AudioClipOff;
|
|
||||||
audioScript.audioClip3 = AudioClipFlicker;
|
|
||||||
}
|
|
||||||
if (!animationContainer.TryGetComponent<AudioSource>(out var audioSource))
|
|
||||||
{
|
|
||||||
// Copy from an existing AudioSource of another light animator
|
|
||||||
var otherSource = tile.gameObject.GetComponentInChildren<AudioSource>();
|
|
||||||
if (otherSource != null)
|
|
||||||
{
|
|
||||||
audioSource = animationContainer.AddComponent<AudioSource>();
|
|
||||||
audioSource.spatialBlend = 1;
|
|
||||||
audioSource.playOnAwake = false;
|
|
||||||
audioSource.outputAudioMixerGroup = otherSource.outputAudioMixerGroup;
|
|
||||||
audioSource.spread = otherSource.spread;
|
|
||||||
audioSource.rolloffMode = otherSource.rolloffMode;
|
|
||||||
audioSource.maxDistance = otherSource.maxDistance;
|
|
||||||
audioSource.SetCustomCurve(AudioSourceCurveType.CustomRolloff, otherSource.GetCustomCurve(AudioSourceCurveType.CustomRolloff));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animator == null)
|
if (animator == null)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -202,89 +123,21 @@ namespace MuzikaGromche
|
||||||
#pragma warning restore CS0162 // Unreachable code detected
|
#pragma warning restore CS0162 // Unreachable code detected
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.ManualPatch?.Invoke(animationContainer);
|
|
||||||
|
|
||||||
animator.runtimeAnimatorController = patch.AnimatorController;
|
animator.runtimeAnimatorController = patch.AnimatorController;
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ManualPatch RenameGameObjectPatch(string relativePath, string newName) => animatorContainer =>
|
|
||||||
{
|
|
||||||
var targetObject = animatorContainer.transform.Find(relativePath)?.gameObject;
|
|
||||||
if (targetObject == null)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
throw new NullReferenceException($"{animatorContainer.name}/{relativePath}: GameObject not found!");
|
|
||||||
#endif
|
|
||||||
#pragma warning disable CS0162 // Unreachable code detected
|
|
||||||
return;
|
|
||||||
#pragma warning restore CS0162 // Unreachable code detected
|
|
||||||
}
|
|
||||||
targetObject.name = newName;
|
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {animatorContainer.name}/{relativePath}: Renamed GameObject");
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Tile))]
|
[HarmonyPatch(typeof(Tile))]
|
||||||
static class PoweredLightsAnimatorsPatch
|
internal class PoweredLightsAnimatorsPatch
|
||||||
{
|
{
|
||||||
[HarmonyPatch(nameof(Tile.AddTriggerVolume))]
|
[HarmonyPatch("AddTriggerVolume")]
|
||||||
[HarmonyPostfix]
|
[HarmonyPostfix]
|
||||||
static void OnAddTriggerVolume(Tile __instance)
|
public static void OnAddTriggerVolume(Tile __instance)
|
||||||
{
|
{
|
||||||
PoweredLightsAnimators.Patch(__instance);
|
PoweredLightsAnimators.Patch(__instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPatch(typeof(RoundManager))]
|
|
||||||
static class AllPoweredLightsPatch
|
|
||||||
{
|
|
||||||
// Vanilla method assumes that GameObjects with tag "PoweredLight" only contain a single Light component each.
|
|
||||||
// This is, however, not true for certains double-light setups, such as:
|
|
||||||
// - double PointLight (even though one of them is 'Point' another is 'Spot') inside CeilingFanAnimContainer in BedroomTile/BedroomTileB;
|
|
||||||
// - MineshaftSpotlight when it has not only `Point Light` but also `IndirectLight` in BirthdayRoomTile;
|
|
||||||
// - (maybe more?)
|
|
||||||
// In order to fix that, replace singular GetComponentInChildren<Light> with plural GetComponentsInChildren<Light> version.
|
|
||||||
[HarmonyPatch(nameof(RoundManager.RefreshLightsList))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
static bool OnRefreshLightsList(RoundManager __instance)
|
|
||||||
{
|
|
||||||
RefreshLightsListPatched(__instance);
|
|
||||||
// Skip the original method
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RefreshLightsListPatched(RoundManager self)
|
|
||||||
{
|
|
||||||
// Reusable list to reduce allocations
|
|
||||||
List<Light> lights = [];
|
|
||||||
|
|
||||||
self.allPoweredLights.Clear();
|
|
||||||
self.allPoweredLightsAnimators.Clear();
|
|
||||||
|
|
||||||
GameObject[] gameObjects = GameObject.FindGameObjectsWithTag("PoweredLight");
|
|
||||||
if (gameObjects == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var gameObject in gameObjects)
|
|
||||||
{
|
|
||||||
Animator animator = gameObject.GetComponentInChildren<Animator>();
|
|
||||||
if (!(animator == null))
|
|
||||||
{
|
|
||||||
self.allPoweredLightsAnimators.Add(animator);
|
|
||||||
// Patched section: Use list instead of singular GetComponentInChildren<Light>
|
|
||||||
gameObject.GetComponentsInChildren(includeInactive: true, lights);
|
|
||||||
self.allPoweredLights.AddRange(lights);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var animator in self.allPoweredLightsAnimators)
|
|
||||||
{
|
|
||||||
animator.SetFloat("flickerSpeed", UnityEngine.Random.Range(0.6f, 1.4f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace MuzikaGromche
|
|
||||||
{
|
|
||||||
[HarmonyPatch(typeof(RoundManager))]
|
|
||||||
static class SpawnRatePatch
|
|
||||||
{
|
|
||||||
const string JesterEnemyName = "Jester";
|
|
||||||
|
|
||||||
// If set to null, do not override spawn chances. Otherwise, it is an index of Jester in RoundManager.currentLevel.Enemies
|
|
||||||
static int? JesterEnemyIndex = null;
|
|
||||||
static float? SpawnTime = null;
|
|
||||||
|
|
||||||
[HarmonyPatch(nameof(RoundManager.AssignRandomEnemyToVent))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
static void AssignRandomEnemyToVentPrefix(RoundManager __instance, EnemyVent vent, float spawnTime)
|
|
||||||
{
|
|
||||||
if (!Config.OverrideSpawnRates.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = __instance.currentLevel.Enemies.FindIndex(enemy => enemy.enemyType.enemyName == JesterEnemyName);
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JesterEnemyIndex = index;
|
|
||||||
SpawnTime = spawnTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(nameof(RoundManager.AssignRandomEnemyToVent))]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
static void AssignRandomEnemyToVentPostfix(RoundManager __instance, EnemyVent vent, float spawnTime)
|
|
||||||
{
|
|
||||||
JesterEnemyIndex = null;
|
|
||||||
SpawnTime = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(nameof(RoundManager.GetRandomWeightedIndex))]
|
|
||||||
[HarmonyPostfix]
|
|
||||||
static void GetRandomWeightedIndexPostfix(RoundManager __instance, ref int[] weights, ref System.Random randomSeed)
|
|
||||||
{
|
|
||||||
if (JesterEnemyIndex is int index && SpawnTime is float spawnTime)
|
|
||||||
{
|
|
||||||
if (__instance.EnemyCannotBeSpawned(index))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var minMultiplierTime = 3 * __instance.timeScript.lengthOfHours;
|
|
||||||
var maxMultiplierTime = 7 * __instance.timeScript.lengthOfHours;
|
|
||||||
var normalizedMultiplierTime = Math.Clamp((spawnTime - minMultiplierTime) / (maxMultiplierTime - minMultiplierTime), 0f, 1f);
|
|
||||||
|
|
||||||
// TODO: Try Expo function instead of Lerp?
|
|
||||||
var minMultiplier = Mathf.Max(1, __instance.minEnemiesToSpawn);
|
|
||||||
var maxMultiplier = 9 + __instance.minEnemiesToSpawn;
|
|
||||||
var multiplier = Mathf.Lerp(minMultiplier, maxMultiplier, normalizedMultiplierTime);
|
|
||||||
|
|
||||||
var newWeight = (int)(weights[index] * multiplier);
|
|
||||||
Debug.Log($"{nameof(MuzikaGromche)} Overriding Jester spawn weight {weights[index]} * {multiplier} => {newWeight}");
|
|
||||||
weights[index] = newWeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Loading…
Reference in New Issue