forked from nikita/muzika-gromche
Compare commits
No commits in common. "d59c5a20c145957cb5253c1c83933fb67ec6c18c" and "8a24448cb665ca4c14739fd4e4c43785664fbecf" have entirely different histories.
d59c5a20c1
...
8a24448cb6
BIN
Assets/DiscoKapotIntro.ogg (Stored with Git LFS)
BIN
Assets/DiscoKapotIntro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/DiscoKapotLoop.ogg (Stored with Git LFS)
BIN
Assets/DiscoKapotLoop.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/HighLowIntro.ogg (Stored with Git LFS)
BIN
Assets/HighLowIntro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/HighLowLoop.ogg (Stored with Git LFS)
BIN
Assets/HighLowLoop.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/IkWilJeIntro.ogg (Stored with Git LFS)
BIN
Assets/IkWilJeIntro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/IkWilJeLoop.ogg (Stored with Git LFS)
BIN
Assets/IkWilJeLoop.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/PaardenIntro.ogg (Stored with Git LFS)
BIN
Assets/PaardenIntro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/PaardenLoop.ogg (Stored with Git LFS)
BIN
Assets/PaardenLoop.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/PickUpSticks1Intro.ogg (Stored with Git LFS)
BIN
Assets/PickUpSticks1Intro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/PickUpSticks2Intro.ogg (Stored with Git LFS)
BIN
Assets/PickUpSticks2Intro.ogg (Stored with Git LFS)
Binary file not shown.
BIN
Assets/PickUpSticksLoop.ogg (Stored with Git LFS)
BIN
Assets/PickUpSticksLoop.ogg (Stored with Git LFS)
Binary file not shown.
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -1,23 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## MuzikaGromche 1337.9001.4 - v73 Chinese New Year Edition
|
## MuzikaGromche 1337.9001.2
|
||||||
|
|
||||||
- Remastered recently added track IkWilJe using a higher quality source audio and better fitting visual effects.
|
|
||||||
- Adjusted lyrics for PWNED (can't believe it missed an obvious joke).
|
|
||||||
- Added a new track Paarden.
|
|
||||||
- Added a new track DiscoKapot.
|
|
||||||
- Added an accessibility option to reduce the intensity of overly distracting visual effects.
|
|
||||||
- Seasonal content like New Year's songs (IkWilJe, Paarden, DiscoKapot) will only be available for selection during their respective seasons.
|
|
||||||
- Reduced memory usage by almost 400 MB, thanks to loading audio clips on demand (not preloading all tracks at launch).
|
|
||||||
- Added a new track PickUpSticks.
|
|
||||||
|
|
||||||
## MuzikaGromche 1337.9001.3 - v73 Happy New Year Edition
|
|
||||||
|
|
||||||
- Added a new track IkWilJe.
|
|
||||||
|
|
||||||
## MuzikaGromche 1337.9001.2 - v73 Rushed Edition
|
|
||||||
|
|
||||||
- Added a new track HighLow.
|
|
||||||
|
|
||||||
## MuzikaGromche 1337.9001.1 - v73 Music louder Edition
|
## MuzikaGromche 1337.9001.1 - v73 Music louder Edition
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
namespace MuzikaGromche;
|
|
||||||
|
|
||||||
internal static class AudioClipsCacheManager
|
|
||||||
{
|
|
||||||
// Cache of file names to loaded AudioClips.
|
|
||||||
// Cache is cleared at the end of each round.
|
|
||||||
static readonly Dictionary<string, AudioClip> Cache = [];
|
|
||||||
|
|
||||||
// In-flight requests
|
|
||||||
static readonly Dictionary<string, (UnityWebRequest Request, List<Action<AudioClip>> Setters)> Requests = [];
|
|
||||||
|
|
||||||
// Not just isDone status, but also whether all requests have been processed.
|
|
||||||
public static bool AllDone => Requests.Count == 0;
|
|
||||||
|
|
||||||
public static void LoadAudioTrack(IAudioTrack track)
|
|
||||||
{
|
|
||||||
GlobalBehaviour.Instance.StartCoroutine(LoadAudioTrackCoroutine(track));
|
|
||||||
}
|
|
||||||
|
|
||||||
static IEnumerator LoadAudioTrackCoroutine(IAudioTrack track)
|
|
||||||
{
|
|
||||||
List<UnityWebRequest> requests = [];
|
|
||||||
requests.Capacity = 2;
|
|
||||||
|
|
||||||
LoadAudioClip(requests, track.AudioType, track.FileNameIntro, clip => track.LoadedIntro = clip);
|
|
||||||
LoadAudioClip(requests, track.AudioType, track.FileNameLoop, clip => track.LoadedLoop = clip);
|
|
||||||
|
|
||||||
yield return new WaitUntil(() => requests.All(request => request.isDone));
|
|
||||||
|
|
||||||
if (requests.All(request => request.result == UnityWebRequest.Result.Success))
|
|
||||||
{
|
|
||||||
foreach (var request in requests)
|
|
||||||
{
|
|
||||||
foreach (var (fileName, (Request, Setters)) in Requests)
|
|
||||||
{
|
|
||||||
if (request == Request)
|
|
||||||
{
|
|
||||||
Plugin.Log.LogDebug($"Audio clip loaded successfully: {fileName}");
|
|
||||||
var clip = DownloadHandlerAudioClip.GetContent(request);
|
|
||||||
Cache[fileName] = clip;
|
|
||||||
foreach (var setter in Setters)
|
|
||||||
{
|
|
||||||
setter(clip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var failed = Requests.Values.Where(tuple => tuple.Request.result != UnityWebRequest.Result.Success).Select(tuple => tuple.Request.GetUrl());
|
|
||||||
Plugin.Log.LogError("Could not load audio file " + string.Join(", ", failed));
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
foreach (var request in requests)
|
|
||||||
{
|
|
||||||
// collect matching keys first to avoid mutating Requests while iterating it
|
|
||||||
var fileNames = Requests
|
|
||||||
.Where(kv => kv.Value.Request == request)
|
|
||||||
.Select(kv => kv.Key)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
foreach (var fileName in fileNames)
|
|
||||||
{
|
|
||||||
if (Requests.TryGetValue(fileName, out var tuple) && tuple.Request != null)
|
|
||||||
{
|
|
||||||
tuple.Request.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Requests.Remove(fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
||||||
|
|
||||||
static void LoadAudioClip(List<UnityWebRequest> requests, AudioType audioType, string fileName, Action<AudioClip> setter)
|
|
||||||
{
|
|
||||||
if (Cache.TryGetValue(fileName, out var cachedClip))
|
|
||||||
{
|
|
||||||
Plugin.Log.LogDebug($"Found cached audio clip: {fileName}");
|
|
||||||
setter(cachedClip);
|
|
||||||
}
|
|
||||||
else if (Requests.TryGetValue(fileName, out var tuple))
|
|
||||||
{
|
|
||||||
Plugin.Log.LogDebug($"Found existing in-flight request for audio clip: {fileName}");
|
|
||||||
tuple.Setters.Add(setter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Plugin.Log.LogDebug($"Sending request to load audio clip: {fileName}");
|
|
||||||
var request = UnityWebRequestMultimedia.GetAudioClip($"file://{dir}/{fileName}", audioType);
|
|
||||||
request.SendWebRequest();
|
|
||||||
Requests[fileName] = (request, [setter]);
|
|
||||||
requests.Add(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Clear()
|
|
||||||
{
|
|
||||||
// Iterate over LoadedClipsCache keys and values, cross join with Plugin.Tracks list,
|
|
||||||
// find AudioTracks that reference the key (file name), and null their corresponding loaded tracks;
|
|
||||||
// then destroy tracks and clear the cache.
|
|
||||||
|
|
||||||
Plugin.Log.LogDebug($"Clearing {Cache.Count} cached audio clips and {Requests.Count} pending requests");
|
|
||||||
|
|
||||||
if (Cache.Count > 0)
|
|
||||||
{
|
|
||||||
var allTracks = Plugin.Tracks.SelectMany(t => t.GetTracks()).ToArray();
|
|
||||||
|
|
||||||
foreach (var (fileName, clip) in Cache)
|
|
||||||
{
|
|
||||||
foreach (var track in allTracks)
|
|
||||||
{
|
|
||||||
// Null out any references to this clip on matching file names.
|
|
||||||
if (track.FileNameIntro == fileName)
|
|
||||||
{
|
|
||||||
track.LoadedIntro = null;
|
|
||||||
}
|
|
||||||
if (track.FileNameLoop == fileName)
|
|
||||||
{
|
|
||||||
track.LoadedLoop = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clip != null)
|
|
||||||
{
|
|
||||||
UnityEngine.Object.Destroy(clip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (fileName, (Request, Setters)) in Requests)
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
Request.Abort();
|
|
||||||
Request.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Requests.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(RoundManager))]
|
|
||||||
static class ClearAudioClipCachePatch
|
|
||||||
{
|
|
||||||
[HarmonyPatch(nameof(RoundManager.DespawnPropsAtEndOfRound))]
|
|
||||||
[HarmonyPatch(nameof(RoundManager.OnDestroy))]
|
|
||||||
[HarmonyPrefix]
|
|
||||||
static void OnDestroy(RoundManager __instance)
|
|
||||||
{
|
|
||||||
var _ = __instance;
|
|
||||||
AudioClipsCacheManager.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -82,7 +82,7 @@ namespace MuzikaGromche
|
||||||
CachedDiscoBalls.Add(discoBall);
|
CachedDiscoBalls.Add(discoBall);
|
||||||
discoBall.SetActive(false);
|
discoBall.SetActive(false);
|
||||||
|
|
||||||
Plugin.Log.LogDebug($"{nameof(DiscoBallManager)} Patched tile '{tile.gameObject.name}'");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Patched tile '{tile.gameObject.name}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<Animator> FindDiscoBallAnimators(GameObject discoBall)
|
static IEnumerable<Animator> FindDiscoBallAnimators(GameObject discoBall)
|
||||||
|
|
@ -109,7 +109,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
public static void Toggle(bool on)
|
public static void Toggle(bool on)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogDebug($"{nameof(DiscoBallManager)} Toggle {(on ? "ON" : "OFF")} {CachedDiscoBallAnimators.Count} animators");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Toggle {(on ? "ON" : "OFF")} {CachedDiscoBallAnimators.Count} animators");
|
||||||
|
|
||||||
foreach (var discoBall in CachedDiscoBalls)
|
foreach (var discoBall in CachedDiscoBalls)
|
||||||
{
|
{
|
||||||
|
|
@ -133,7 +133,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
internal static void Clear()
|
internal static void Clear()
|
||||||
{
|
{
|
||||||
Plugin.Log.LogDebug($"{nameof(DiscoBallManager)} Clearing {CachedDiscoBalls.Count} disco balls & {CachedDiscoBallAnimators.Count} animators");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(DiscoBallManager)} Clearing {CachedDiscoBalls.Count} disco balls & {CachedDiscoBallAnimators.Count} animators");
|
||||||
CachedDiscoBallAnimators.Clear();
|
CachedDiscoBallAnimators.Clear();
|
||||||
CachedDiscoBalls.Clear();
|
CachedDiscoBalls.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
var jsonObject = new Dictionary<string, object>();
|
var jsonObject = new Dictionary<string, object>();
|
||||||
var tracksList = new List<object>();
|
var tracksList = new List<object>();
|
||||||
jsonObject["version"] = MyPluginInfo.PLUGIN_VERSION;
|
jsonObject["version"] = PluginInfo.PLUGIN_VERSION;
|
||||||
jsonObject["tracks"] = tracksList;
|
jsonObject["tracks"] = tracksList;
|
||||||
foreach (var (selectableTrack, audioTrack) in SelectTracks(tracks))
|
foreach (var (selectableTrack, audioTrack) in SelectTracks(tracks))
|
||||||
{
|
{
|
||||||
|
|
@ -56,15 +56,14 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
["Name"] = audioTrack.Name, // may be different from selectableTrack.Name, if selectable track is a group
|
["Name"] = audioTrack.Name, // may be different from selectableTrack.Name, if selectable track is a group
|
||||||
["IsExplicit"] = selectableTrack.IsExplicit,
|
["IsExplicit"] = selectableTrack.IsExplicit,
|
||||||
["Season"] = selectableTrack.Season?.Name,
|
|
||||||
["Language"] = selectableTrack.Language.Full,
|
["Language"] = selectableTrack.Language.Full,
|
||||||
["WindUpTimer"] = audioTrack.WindUpTimer,
|
["WindUpTimer"] = audioTrack.WindUpTimer,
|
||||||
["Bpm"] = audioTrack.Bpm,
|
["Bpm"] = audioTrack.Bpm,
|
||||||
["Beats"] = audioTrack.Beats,
|
["Beats"] = audioTrack.Beats,
|
||||||
["LoopOffset"] = audioTrack.LoopOffset,
|
["LoopOffset"] = audioTrack.LoopOffset,
|
||||||
["Ext"] = audioTrack.Ext,
|
["Ext"] = audioTrack.Ext,
|
||||||
["FileDurationIntro"] = audioTrack.LoadedIntro?.length ?? 0f,
|
["FileDurationIntro"] = audioTrack.LoadedIntro.length,
|
||||||
["FileDurationLoop"] = audioTrack.LoadedLoop?.length ?? 0f,
|
["FileDurationLoop"] = audioTrack.LoadedLoop.length,
|
||||||
["FileNameIntro"] = audioTrack.FileNameIntro,
|
["FileNameIntro"] = audioTrack.FileNameIntro,
|
||||||
["FileNameLoop"] = audioTrack.FileNameLoop,
|
["FileNameLoop"] = audioTrack.FileNameLoop,
|
||||||
["BeatsOffset"] = audioTrack.BeatsOffset,
|
["BeatsOffset"] = audioTrack.BeatsOffset,
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace MuzikaGromche;
|
|
||||||
|
|
||||||
// A global MonoBehaviour instance to run coroutines from non-MonoBehaviour or static context.
|
|
||||||
internal static class GlobalBehaviour
|
|
||||||
{
|
|
||||||
sealed class AdhocBehaviour : MonoBehaviour;
|
|
||||||
|
|
||||||
static AdhocBehaviour? instance = null;
|
|
||||||
|
|
||||||
public static MonoBehaviour Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
{
|
|
||||||
var go = new GameObject("MuzikaGromche_GlobalBehaviour", [
|
|
||||||
typeof(AdhocBehaviour),
|
|
||||||
])
|
|
||||||
{
|
|
||||||
hideFlags = HideFlags.HideAndDontSave
|
|
||||||
};
|
|
||||||
Object.DontDestroyOnLoad(go);
|
|
||||||
instance = go.GetComponent<AdhocBehaviour>();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,11 +8,14 @@
|
||||||
<AssemblyName>Ratijas.MuzikaGromche</AssemblyName>
|
<AssemblyName>Ratijas.MuzikaGromche</AssemblyName>
|
||||||
<Product>Muzika Gromche</Product>
|
<Product>Muzika Gromche</Product>
|
||||||
<Description>Add some content to your inverse teleporter experience on Titan!</Description>
|
<Description>Add some content to your inverse teleporter experience on Titan!</Description>
|
||||||
<Version>1337.9001.4</Version>
|
<Version>1337.9001.2</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<!-- NetcodePatch requires anything but 'full' -->
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
|
||||||
<PackageReadmeFile>../README.md</PackageReadmeFile>
|
<PackageReadmeFile>../README.md</PackageReadmeFile>
|
||||||
<PackageProjectUrl>https://git.vilunov.me/ratijas/muzika-gromche</PackageProjectUrl>
|
<PackageProjectUrl>https://git.vilunov.me/ratijas/muzika-gromche</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://git.vilunov.me/ratijas/muzika-gromche</RepositoryUrl>
|
<RepositoryUrl>https://git.vilunov.me/ratijas/muzika-gromche</RepositoryUrl>
|
||||||
|
|
@ -29,20 +32,10 @@
|
||||||
<NoWarn>$(NoWarn);CS0436</NoWarn>
|
<NoWarn>$(NoWarn);CS0436</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Embedded debug -->
|
|
||||||
<PropertyGroup>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<!-- NetcodePatch requires anything but 'full' -->
|
|
||||||
<DebugType>embedded</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="$(Configuration) == 'Release'">
|
|
||||||
<PathMap>$(UserProfile)=~,$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=$(PackageId)/</PathMap>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BepInEx.Analyzers" Version="1.*" PrivateAssets="all" Private="false" />
|
<PackageReference Include="BepInEx.Analyzers" Version="1.*" PrivateAssets="all" Private="false" />
|
||||||
<PackageReference Include="BepInEx.Core" Version="5.*" PrivateAssets="all" Private="false" />
|
<PackageReference Include="BepInEx.Core" Version="5.*" PrivateAssets="all" Private="false" />
|
||||||
<PackageReference Include="BepInEx.PluginInfoProps" Version="2.*" PrivateAssets="all"/>
|
<PackageReference Include="BepInEx.PluginInfoProps" Version="1.*" PrivateAssets="all" Private="false" />
|
||||||
<PackageReference Include="UnityEngine.Modules" Version="2022.3.9" PrivateAssets="all" Private="false" />
|
<PackageReference Include="UnityEngine.Modules" Version="2022.3.9" PrivateAssets="all" Private="false" />
|
||||||
<PackageReference Include="BepInEx.AssemblyPublicizer.MSBuild" Version="0.4.1" PrivateAssets="all" Private="false" />
|
<PackageReference Include="BepInEx.AssemblyPublicizer.MSBuild" Version="0.4.1" PrivateAssets="all" Private="false" />
|
||||||
<PackageReference Include="AinaVT-LethalConfig" Version="1.4.6" PrivateAssets="all" Private="false" />
|
<PackageReference Include="AinaVT-LethalConfig" Version="1.4.6" PrivateAssets="all" Private="false" />
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using BepInEx;
|
using BepInEx;
|
||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using BepInEx.Logging;
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using LethalConfig;
|
using LethalConfig;
|
||||||
using LethalConfig.ConfigItems;
|
using LethalConfig.ConfigItems;
|
||||||
|
|
@ -8,6 +7,7 @@ using LethalConfig.ConfigItems.Options;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|
@ -16,17 +16,16 @@ using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
namespace MuzikaGromche
|
namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
|
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
|
||||||
[BepInDependency("ainavt.lc.lethalconfig", "1.4.6")]
|
[BepInDependency("ainavt.lc.lethalconfig", "1.4.6")]
|
||||||
[BepInDependency("watergun.v72lightfix", BepInDependency.DependencyFlags.SoftDependency)]
|
[BepInDependency("watergun.v72lightfix", BepInDependency.DependencyFlags.SoftDependency)]
|
||||||
[BepInDependency("BMX.LobbyCompatibility", BepInDependency.DependencyFlags.SoftDependency)]
|
[BepInDependency("BMX.LobbyCompatibility", BepInDependency.DependencyFlags.SoftDependency)]
|
||||||
public class Plugin : BaseUnityPlugin
|
public class Plugin : BaseUnityPlugin
|
||||||
{
|
{
|
||||||
private static Harmony Harmony = null!;
|
|
||||||
internal static ManualLogSource Log = null!;
|
|
||||||
internal new static Config Config { get; private set; } = null!;
|
internal new static Config Config { get; private set; } = null!;
|
||||||
|
|
||||||
// Not all lights are white by default. For example, Mineshaft's neon light is green-ish.
|
// Not all lights are white by default. For example, Mineshaft's neon light is green-ish.
|
||||||
|
|
@ -47,15 +46,6 @@ namespace MuzikaGromche
|
||||||
[-0.5f, 0.5f, 8f, 15f, 16f, 24f, 29f, 30f, 36f, 37f, 38f, 44f, 47.5f],
|
[-0.5f, 0.5f, 8f, 15f, 16f, 24f, 29f, 30f, 36f, 37f, 38f, 44f, 47.5f],
|
||||||
[ 0f, 0.6f, 0f, 0f, 0.4f, 0f, 0f, 0.3f, 0f, 0f, 0.3f, 0f, 0f]);
|
[ 0f, 0.6f, 0f, 0f, 0.4f, 0f, 0f, 0.3f, 0f, 0f, 0.3f, 0f, 0f]);
|
||||||
|
|
||||||
private static readonly Palette PalettePickUpSticks = Palette
|
|
||||||
.Parse(["#FC933C", "#FC3C9D", "#EEA0A5", "#CA71FC", "#d01760"])
|
|
||||||
.Use(p =>
|
|
||||||
{
|
|
||||||
var energetic = p * 6 + new Palette(p.Colors[0..2]); // 32 colors
|
|
||||||
var slower = (p * 3 + new Palette([p.Colors[2]])).Stretch(2); // 16*2 colors
|
|
||||||
return energetic + slower;
|
|
||||||
});
|
|
||||||
|
|
||||||
public static readonly ISelectableTrack[] Tracks = [
|
public static readonly ISelectableTrack[] Tracks = [
|
||||||
new SelectableAudioTrack
|
new SelectableAudioTrack
|
||||||
{
|
{
|
||||||
|
|
@ -468,8 +458,8 @@ namespace MuzikaGromche
|
||||||
(56, "Counting crypto to\nembarrass Wall Street"),
|
(56, "Counting crypto to\nembarrass Wall Street"),
|
||||||
(80, $"Instling min3r.exe\t\t\tresolving ur private IP\n/"),
|
(80, $"Instling min3r.exe\t\t\tresolving ur private IP\n/"),
|
||||||
(82, $"Instling min3r.exe\n00% [8=D ]\tHenllo ${{username = \"{Environment.UserName}\"}}\t\tresolving ur private IP\n-{PwnLyricsVariants[^3]}"),
|
(82, $"Instling min3r.exe\n00% [8=D ]\tHenllo ${{username = \"{Environment.UserName}\"}}\t\tresolving ur private IP\n-{PwnLyricsVariants[^3]}"),
|
||||||
(84, $"Instling min3r.exe\n34% [8====D ]\t\t\tresolving ur private IP\n\\{PwnLyricsVariants[^3]}"),
|
(84, $"Instling min3r.exe\n33% [8====D ]\t\t\tresolving ur private IP\n\\{PwnLyricsVariants[^3]}"),
|
||||||
(86, $"Instling min3r.exe\n69% [8=========D ]\t\t\tresolving ur private IP\n|{PwnLyricsVariants[^2]}"),
|
(86, $"Instling min3r.exe\n66% [8=========D ]\t\t\tresolving ur private IP\n|{PwnLyricsVariants[^2]}"),
|
||||||
(88, $"Instling min3r.exe\n95% [8============D ]\t\tWhere did you download\nthis < mod / dll > from?\tresolving ur private IP\n{PwnLyricsVariants[^2]}/"),
|
(88, $"Instling min3r.exe\n95% [8============D ]\t\tWhere did you download\nthis < mod / dll > from?\tresolving ur private IP\n{PwnLyricsVariants[^2]}/"),
|
||||||
(90, $"Instling min3r.exe\n99% [8=============D]\t\t\tresolving ur private IP\n-{PwnLyricsVariants[^2]}"),
|
(90, $"Instling min3r.exe\n99% [8=============D]\t\t\tresolving ur private IP\n-{PwnLyricsVariants[^2]}"),
|
||||||
(92, $"Encrpt1ng f!les.. \n99% [8=============D]\t\t\tresolving ur private IP\n\\{PwnLyricsVariants[^1]}"),
|
(92, $"Encrpt1ng f!les.. \n99% [8=============D]\t\t\tresolving ur private IP\n\\{PwnLyricsVariants[^1]}"),
|
||||||
|
|
@ -857,192 +847,17 @@ namespace MuzikaGromche
|
||||||
[0f, 0.5f, 0f, 0f, 0.5f]),
|
[0f, 0.5f, 0f, 0f, 0.5f]),
|
||||||
GameOverText = "[LIFE SUPPORT: REAL GONE]",
|
GameOverText = "[LIFE SUPPORT: REAL GONE]",
|
||||||
},
|
},
|
||||||
new SelectableAudioTrack
|
|
||||||
{
|
|
||||||
Name = "HighLow",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
Language = Language.ENGLISH,
|
|
||||||
WindUpTimer = 37.12f,
|
|
||||||
Bars = 12,
|
|
||||||
BeatsOffset = 0f,
|
|
||||||
ColorTransitionIn = 0.75f,
|
|
||||||
ColorTransitionOut = 0.25f,
|
|
||||||
ColorTransitionEasing = Easing.OutExpo,
|
|
||||||
Palette = Palette.Parse([
|
|
||||||
"#2e2e28", "#dfa24d", "#2e2e28", "#dfa24d",
|
|
||||||
"#2e2e28", "#dfa24d", "#2e2e28", "#dfa24d",
|
|
||||||
]),
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -1.5f,
|
|
||||||
FadeOutDuration = 1.5f,
|
|
||||||
FlickerLightsTimeSeries = [-33, 39],
|
|
||||||
Lyrics = [
|
|
||||||
],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new(
|
|
||||||
[-2f, -1f, 6f],
|
|
||||||
[ 0f, 0.5f, 0f]),
|
|
||||||
CondensationLoopOffsetTimeSeries = new(
|
|
||||||
[-2f, -1f, 6f],
|
|
||||||
[ 0f, 0.5f, 0f]),
|
|
||||||
GameOverText = "[LIFE SUPORT: NIRVANA]",
|
|
||||||
},
|
|
||||||
new SelectableAudioTrack
|
|
||||||
{
|
|
||||||
Name = "IkWilJe",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
Language = Language.ENGLISH,
|
|
||||||
Season = Season.NewYear,
|
|
||||||
WindUpTimer = 43.03f,
|
|
||||||
Beats = 13 * 4 + 2, // = 54
|
|
||||||
BeatsOffset = 0f,
|
|
||||||
ColorTransitionIn = 0.01f,
|
|
||||||
ColorTransitionOut = 0.99f,
|
|
||||||
ColorTransitionEasing = Easing.OutExpo,
|
|
||||||
Palette = Palette.Parse([
|
|
||||||
"#0B6623", "#FF2D2D", "#FFD700",
|
|
||||||
"#00BFFF", "#9400D3", "#00FF7F",
|
|
||||||
]),
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -14f,
|
|
||||||
FadeOutDuration = 12f,
|
|
||||||
FlickerLightsTimeSeries = [31.45f],
|
|
||||||
Lyrics = [],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new(
|
|
||||||
[0f, 0.25f, 6f],
|
|
||||||
[0f, 0.5f, 0f]),
|
|
||||||
GameOverText = "[NEXT YEAR -- DEFINITELY]",
|
|
||||||
},
|
|
||||||
new SelectableAudioTrack
|
|
||||||
{
|
|
||||||
Name = "Paarden",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
Language = Language.RUSSIAN,
|
|
||||||
Season = Season.NewYear,
|
|
||||||
WindUpTimer = 36.12f,
|
|
||||||
Bars = 8,
|
|
||||||
BeatsOffset = 0f,
|
|
||||||
ColorTransitionIn = 0.25f,
|
|
||||||
ColorTransitionOut = 0.4f,
|
|
||||||
ColorTransitionEasing = Easing.OutCubic,
|
|
||||||
Palette = Palette.Parse([
|
|
||||||
"#F0FBFF", "#9ED9FF", "#0B95FF",
|
|
||||||
"#66C7FF", "#CAE8FF", "#3BB6FF",
|
|
||||||
]),
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -4f,
|
|
||||||
FadeOutDuration = 4f,
|
|
||||||
FlickerLightsTimeSeries = [31.5f],
|
|
||||||
Lyrics = [],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new(
|
|
||||||
[0f, 0.25f, 6f],
|
|
||||||
[0f, 0.5f, 0f]),
|
|
||||||
GameOverText = "[NEXT YEAR -- DEFINITELY]",
|
|
||||||
},
|
|
||||||
new SelectableAudioTrack
|
|
||||||
{
|
|
||||||
Name = "DiscoKapot",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
Language = Language.RUSSIAN,
|
|
||||||
Season = Season.NewYear,
|
|
||||||
WindUpTimer = 30.3f,
|
|
||||||
Bars = 8,
|
|
||||||
BeatsOffset = 0f,
|
|
||||||
ColorTransitionIn = 0.25f,
|
|
||||||
ColorTransitionOut = 0.6f,
|
|
||||||
ColorTransitionEasing = Easing.InOutExpo,
|
|
||||||
Palette = Palette.Parse([
|
|
||||||
"#0B6623", "#FF2D2D", "#FFD700",
|
|
||||||
"#00BFFF", "#9400D3", "#00FF7F",
|
|
||||||
]),
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -4f,
|
|
||||||
FadeOutDuration = 4f,
|
|
||||||
FlickerLightsTimeSeries = [-32, -24, -16, 16, 32],
|
|
||||||
Lyrics = [],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new(
|
|
||||||
[0f, 0.25f, 6f],
|
|
||||||
[0f, 0.5f, 0f]),
|
|
||||||
GameOverText = "[NEXT YEAR -- DEFINITELY]",
|
|
||||||
},
|
|
||||||
new SelectableTracksGroup
|
|
||||||
{
|
|
||||||
Name = "PickUpSticks",
|
|
||||||
Language = Language.ENGLISH,
|
|
||||||
Tracks =
|
|
||||||
[
|
|
||||||
new CoreAudioTrack
|
|
||||||
{
|
|
||||||
Name = "PickUpSticks1",
|
|
||||||
FileNameLoop = "PickUpSticksLoop.ogg",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
WindUpTimer = 38.5f,
|
|
||||||
Bars = 16,
|
|
||||||
BeatsOffset = 0.2f,
|
|
||||||
ColorTransitionIn = 0.6f,
|
|
||||||
ColorTransitionOut = 0.3f,
|
|
||||||
ColorTransitionEasing = Easing.InOutCubic,
|
|
||||||
Palette = PalettePickUpSticks,
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -2,
|
|
||||||
FadeOutDuration = 2,
|
|
||||||
FlickerLightsTimeSeries = [-36, -4, 32],
|
|
||||||
Lyrics = [],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new([0f, 0.5f, 3f, 32f, 34f, 40f], [0f, 0.5f, 0f, 0f, 0.3f, 0f]),
|
|
||||||
CondensationLoopOffsetTimeSeries = new([23f, 28f, 31f, 34f, 38f, 52f], [0f, 0.6f, 0f, 0f, 0.7f, 0f]),
|
|
||||||
GameOverText = "[LOVE SUPPORT: OFFLINE]",
|
|
||||||
},
|
|
||||||
new CoreAudioTrack
|
|
||||||
{
|
|
||||||
Name = "PickUpSticks2",
|
|
||||||
FileNameLoop = "PickUpSticksLoop.ogg",
|
|
||||||
AudioType = AudioType.OGGVORBIS,
|
|
||||||
WindUpTimer = 38.47f,
|
|
||||||
Bars = 16,
|
|
||||||
BeatsOffset = 0.2f,
|
|
||||||
ColorTransitionIn = 0.6f,
|
|
||||||
ColorTransitionOut = 0.3f,
|
|
||||||
ColorTransitionEasing = Easing.InOutCubic,
|
|
||||||
Palette = PalettePickUpSticks,
|
|
||||||
LoopOffset = 0,
|
|
||||||
FadeOutBeat = -2,
|
|
||||||
FadeOutDuration = 2,
|
|
||||||
FlickerLightsTimeSeries = [-36, -4, 32],
|
|
||||||
Lyrics = [],
|
|
||||||
DrunknessLoopOffsetTimeSeries = new([0f, 0.5f, 3f, 32f, 34f, 40f], [0f, 0.5f, 0f, 0f, 0.3f, 0f]),
|
|
||||||
CondensationLoopOffsetTimeSeries = new([23f, 28f, 31f, 34f, 38f, 52f], [0f, 0.5f, 0f, 0f, 0.5f, 0f]),
|
|
||||||
GameOverText = "[LOVE SUPPORT: OFFLINE]",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private static int GetCurrentSeed()
|
|
||||||
{
|
|
||||||
var seed = 0;
|
|
||||||
var roundManager = RoundManager.Instance;
|
|
||||||
if (roundManager != null && roundManager.dungeonGenerator != null)
|
|
||||||
{
|
|
||||||
seed = roundManager.dungeonGenerator.Generator.ChosenSeed;
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ISelectableTrack ChooseTrack()
|
public static ISelectableTrack ChooseTrack()
|
||||||
{
|
{
|
||||||
var seed = GetCurrentSeed();
|
var seed = RoundManager.Instance.dungeonGenerator.Generator.ChosenSeed;
|
||||||
var today = DateTime.Today;
|
var tracks = Config.SkipExplicitTracks.Value ? [.. Tracks.Where(track => !track.IsExplicit)] : Tracks;
|
||||||
var season = SeasonalContentManager.CurrentSeason(today);
|
int[] weights = [.. tracks.Select(track => track.Weight.Value)];
|
||||||
var tracksEnumerable = SeasonalContentManager.Filter(Tracks, season);
|
|
||||||
if (Config.SkipExplicitTracks.Value)
|
|
||||||
{
|
|
||||||
tracksEnumerable = tracksEnumerable.Where(track => !track.IsExplicit);
|
|
||||||
}
|
|
||||||
var tracks = tracksEnumerable.ToArray();
|
|
||||||
int[] weights = tracks.Select(track => track.Weight.Value).ToArray();
|
|
||||||
var rwi = new RandomWeightedIndex(weights);
|
var rwi = new RandomWeightedIndex(weights);
|
||||||
var trackId = rwi.GetRandomWeightedIndex(seed);
|
var trackId = rwi.GetRandomWeightedIndex(seed);
|
||||||
var track = tracks[trackId];
|
var track = tracks[trackId];
|
||||||
Log.LogInfo($"Seed is {seed}, season is {season?.Name ?? "<none>"}, chosen track is \"{track.Name}\", #{trackId} of {rwi}");
|
Debug.Log($"{nameof(MuzikaGromche)} Seed is {seed}, chosen track is \"{track.Name}\", #{trackId} of {rwi}");
|
||||||
return tracks[trackId];
|
return tracks[trackId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1075,51 +890,77 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
Log = Logger;
|
|
||||||
|
|
||||||
// Sort in place by name
|
// Sort in place by name
|
||||||
Array.Sort(Tracks.Select(track => track.Name).ToArray(), Tracks);
|
Array.Sort(Tracks.Select(track => track.Name).ToArray(), Tracks);
|
||||||
|
|
||||||
|
string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
Dictionary<string, (UnityWebRequest Request, List<Action<AudioClip>> Setters)> requests = [];
|
||||||
|
requests.EnsureCapacity(Tracks.Length * 2);
|
||||||
|
|
||||||
|
foreach (var track in Tracks.SelectMany(track => track.GetTracks()))
|
||||||
|
{
|
||||||
|
foreach (var (fileName, setter) in new (string, Action<AudioClip>)[]
|
||||||
|
{
|
||||||
|
(track.FileNameIntro, clip => track.LoadedIntro = clip),
|
||||||
|
(track.FileNameLoop, clip => track.LoadedLoop = clip),
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if (requests.TryGetValue(fileName, out var tuple))
|
||||||
|
{
|
||||||
|
tuple.Setters.Add(setter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var request = UnityWebRequestMultimedia.GetAudioClip($"file://{dir}/{fileName}", track.AudioType);
|
||||||
|
request.SendWebRequest();
|
||||||
|
requests[fileName] = (request, [setter]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!requests.Values.All(tuple => tuple.Request.isDone)) { }
|
||||||
|
|
||||||
|
if (requests.Values.All(tuple => tuple.Request.result == UnityWebRequest.Result.Success))
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (var (fileName, tuple) in requests)
|
||||||
|
{
|
||||||
|
var clip = DownloadHandlerAudioClip.GetContent(tuple.Request);
|
||||||
|
foreach (var setter in tuple.Setters)
|
||||||
|
{
|
||||||
|
setter(clip);
|
||||||
|
}
|
||||||
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
GlobalBehaviour.Instance.StartCoroutine(PreloadDebugAndExport(Tracks));
|
foreach (var track in Tracks)
|
||||||
|
{
|
||||||
|
track.Debug();
|
||||||
|
}
|
||||||
|
Exporter.ExportTracksJSON(Tracks);
|
||||||
#endif
|
#endif
|
||||||
Config = new Config(base.Config);
|
Config = new Config(base.Config);
|
||||||
DiscoBallManager.Load();
|
DiscoBallManager.Load();
|
||||||
PoweredLightsAnimators.Load();
|
PoweredLightsAnimators.Load();
|
||||||
Harmony = new Harmony(MyPluginInfo.PLUGIN_NAME);
|
var harmony = new Harmony(PluginInfo.PLUGIN_NAME);
|
||||||
Harmony.PatchAll(typeof(GameNetworkManagerPatch));
|
harmony.PatchAll(typeof(GameNetworkManagerPatch));
|
||||||
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(AllPoweredLightsPatch));
|
||||||
Harmony.PatchAll(typeof(DiscoBallTilePatch));
|
harmony.PatchAll(typeof(DiscoBallTilePatch));
|
||||||
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));
|
harmony.PatchAll(typeof(ScreenFiltersManager.HUDManagerScreenFiltersPatch));
|
||||||
Harmony.PatchAll(typeof(ClearAudioClipCachePatch));
|
|
||||||
NetcodePatcher();
|
NetcodePatcher();
|
||||||
Compatibility.Register(this);
|
Compatibility.Register(this);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
#if DEBUG
|
|
||||||
static IEnumerator PreloadDebugAndExport(ISelectableTrack[] tracks)
|
|
||||||
{
|
{
|
||||||
foreach (var track in tracks.SelectMany(track => track.GetTracks()))
|
var failed = requests.Values.Where(tuple => tuple.Request.result != UnityWebRequest.Result.Success).Select(tuple => tuple.Request.GetUrl());
|
||||||
{
|
Logger.LogError("Could not load audio file " + string.Join(", ", failed));
|
||||||
AudioClipsCacheManager.LoadAudioTrack(track);
|
|
||||||
}
|
}
|
||||||
yield return new WaitUntil(() => AudioClipsCacheManager.AllDone);
|
|
||||||
Log.LogDebug("All tracks preloaded, exporting to JSON");
|
|
||||||
|
|
||||||
foreach (var track in tracks)
|
|
||||||
{
|
|
||||||
track.Debug();
|
|
||||||
}
|
}
|
||||||
Exporter.ExportTracksJSON(tracks);
|
|
||||||
AudioClipsCacheManager.Clear();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private static void NetcodePatcher()
|
private static void NetcodePatcher()
|
||||||
{
|
{
|
||||||
|
|
@ -1251,7 +1092,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
// 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 : ISeasonalContent
|
public interface ISelectableTrack
|
||||||
{
|
{
|
||||||
// Name of the track, as shown in config entry UI; also used for default file names.
|
// Name of the track, as shown in config entry UI; also used for default file names.
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
|
|
@ -1288,47 +1129,21 @@ namespace MuzikaGromche
|
||||||
public float WindUpTimer { get; }
|
public float WindUpTimer { get; }
|
||||||
|
|
||||||
// Estimated number of beats per minute. Not used for light show, but might come in handy.
|
// Estimated number of beats per minute. Not used for light show, but might come in handy.
|
||||||
public float Bpm
|
public float Bpm => 60f / (LoadedLoop.length / Beats);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (LoadedLoop == null || LoadedLoop.length <= 0f)
|
|
||||||
{
|
|
||||||
return 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 60f / (LoadedLoop.length / Beats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// How many beats the loop segment has. The default strategy is to switch color of lights on each beat.
|
// How many beats the loop segment has. The default strategy is to switch color of lights on each beat.
|
||||||
public int Beats { get; }
|
public int Beats { get; }
|
||||||
|
|
||||||
// Number of beats between WindUpTimer and where looped segment starts (not the loop audio).
|
// Number of beats between WindUpTimer and where looped segment starts (not the loop audio).
|
||||||
public int LoopOffset { get; }
|
public int LoopOffset { get; }
|
||||||
public float LoopOffsetInSeconds
|
public float LoopOffsetInSeconds => (float)LoopOffset / (float)Beats * LoadedLoop.length;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (LoadedLoop == null || LoadedLoop.length <= 0f)
|
|
||||||
{
|
|
||||||
return 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (float)LoopOffset / (float)Beats * LoadedLoop.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MPEG is basically mp3, and it can produce gaps at the start.
|
// MPEG is basically mp3, and it can produce gaps at the start.
|
||||||
// WAV is OK, but takes a lot of space. Try OGGVORBIS instead.
|
// WAV is OK, but takes a lot of space. Try OGGVORBIS instead.
|
||||||
public AudioType AudioType { get; }
|
public AudioType AudioType { get; }
|
||||||
|
|
||||||
public AudioClip? LoadedIntro { get; internal set; }
|
public AudioClip LoadedIntro { get; internal set; }
|
||||||
public AudioClip? LoadedLoop { get; internal set; }
|
public AudioClip LoadedLoop { get; internal set; }
|
||||||
|
|
||||||
public string FileNameIntro { get; }
|
public string FileNameIntro { get; }
|
||||||
public string FileNameLoop { get; }
|
public string FileNameLoop { get; }
|
||||||
|
|
@ -1345,20 +1160,7 @@ namespace MuzikaGromche
|
||||||
public float BeatsOffset { get; }
|
public float BeatsOffset { get; }
|
||||||
|
|
||||||
// Offset of beats, in seconds. Bigger offset => colors will change later.
|
// Offset of beats, in seconds. Bigger offset => colors will change later.
|
||||||
public float BeatsOffsetInSeconds
|
public float BeatsOffsetInSeconds => BeatsOffset / Beats * LoadedLoop.length;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (LoadedLoop == null || LoadedLoop.length <= 0f)
|
|
||||||
{
|
|
||||||
return 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return BeatsOffset / (float)Beats * LoadedLoop.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float FadeOutBeat { get; }
|
public float FadeOutBeat { get; }
|
||||||
public float FadeOutDuration { get; }
|
public float FadeOutDuration { get; }
|
||||||
|
|
@ -1397,8 +1199,8 @@ namespace MuzikaGromche
|
||||||
int IAudioTrack.Beats => Track.Beats;
|
int IAudioTrack.Beats => Track.Beats;
|
||||||
int IAudioTrack.LoopOffset => Track.LoopOffset;
|
int IAudioTrack.LoopOffset => Track.LoopOffset;
|
||||||
AudioType IAudioTrack.AudioType => Track.AudioType;
|
AudioType IAudioTrack.AudioType => Track.AudioType;
|
||||||
AudioClip? IAudioTrack.LoadedIntro { get => Track.LoadedIntro; set => Track.LoadedIntro = value; }
|
AudioClip IAudioTrack.LoadedIntro { get => Track.LoadedIntro; set => Track.LoadedIntro = value; }
|
||||||
AudioClip? IAudioTrack.LoadedLoop { get => Track.LoadedLoop; set => Track.LoadedLoop = value; }
|
AudioClip IAudioTrack.LoadedLoop { get => Track.LoadedLoop; set => Track.LoadedLoop = value; }
|
||||||
string IAudioTrack.FileNameIntro => Track.FileNameIntro;
|
string IAudioTrack.FileNameIntro => Track.FileNameIntro;
|
||||||
string IAudioTrack.FileNameLoop => Track.FileNameLoop;
|
string IAudioTrack.FileNameLoop => Track.FileNameLoop;
|
||||||
float IAudioTrack.BeatsOffset => Track.BeatsOffset;
|
float IAudioTrack.BeatsOffset => Track.BeatsOffset;
|
||||||
|
|
@ -1432,8 +1234,8 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
public int LoopOffset { get; init; } = 0;
|
public int LoopOffset { get; init; } = 0;
|
||||||
public AudioType AudioType { get; init; } = AudioType.MPEG;
|
public AudioType AudioType { get; init; } = AudioType.MPEG;
|
||||||
public AudioClip? LoadedIntro { get; set; } = null;
|
public AudioClip LoadedIntro { get; set; } = null!;
|
||||||
public AudioClip? LoadedLoop { get; set; } = null;
|
public AudioClip LoadedLoop { get; set; } = null!;
|
||||||
|
|
||||||
private string? FileNameIntroOverride = null;
|
private string? FileNameIntroOverride = null;
|
||||||
public string FileNameIntro
|
public string FileNameIntro
|
||||||
|
|
@ -1501,7 +1303,6 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
public /* required */ Language Language { get; init; }
|
public /* required */ Language Language { get; init; }
|
||||||
public bool IsExplicit { get; init; } = false;
|
public bool IsExplicit { get; init; } = false;
|
||||||
public Season? Season { get; init; } = null;
|
|
||||||
ConfigEntry<int> ISelectableTrack.Weight { get; set; } = null!;
|
ConfigEntry<int> ISelectableTrack.Weight { get; set; } = null!;
|
||||||
|
|
||||||
IAudioTrack[] ISelectableTrack.GetTracks() => [this];
|
IAudioTrack[] ISelectableTrack.GetTracks() => [this];
|
||||||
|
|
@ -1510,7 +1311,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
void ISelectableTrack.Debug()
|
void ISelectableTrack.Debug()
|
||||||
{
|
{
|
||||||
Plugin.Log.LogDebug($"Track \"{Name}\", Intro={LoadedIntro?.length:N4}, Loop={LoadedLoop?.length:N4}");
|
Debug.Log($"{nameof(MuzikaGromche)} Track \"{Name}\", Intro={LoadedIntro.length:N4}, Loop={LoadedLoop.length:N4}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1519,7 +1320,6 @@ namespace MuzikaGromche
|
||||||
public /* required */ string Name { get; init; } = "";
|
public /* required */ string Name { get; init; } = "";
|
||||||
public /* required */ Language Language { get; init; }
|
public /* required */ Language Language { get; init; }
|
||||||
public bool IsExplicit { get; init; } = false;
|
public bool IsExplicit { get; init; } = false;
|
||||||
public Season? Season { get; init; } = null;
|
|
||||||
ConfigEntry<int> ISelectableTrack.Weight { get; set; } = null!;
|
ConfigEntry<int> ISelectableTrack.Weight { get; set; } = null!;
|
||||||
|
|
||||||
public /* required */ IAudioTrack[] Tracks = [];
|
public /* required */ IAudioTrack[] Tracks = [];
|
||||||
|
|
@ -1537,10 +1337,10 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
void ISelectableTrack.Debug()
|
void ISelectableTrack.Debug()
|
||||||
{
|
{
|
||||||
Plugin.Log.LogDebug($"Track Group \"{Name}\", Count={Tracks.Length}");
|
Debug.Log($"{nameof(MuzikaGromche)} Track Group \"{Name}\", Count={Tracks.Length}");
|
||||||
foreach (var (track, index) in Tracks.Select((x, i) => (x, i)))
|
foreach (var (track, index) in Tracks.Select((x, i) => (x, i)))
|
||||||
{
|
{
|
||||||
Plugin.Log.LogDebug($" Track {index} \"{track.Name}\", Intro={track.LoadedIntro?.length:N4}, Loop={track.LoadedLoop?.length:N4}");
|
Debug.Log($"{nameof(MuzikaGromche)} Track {index} \"{track.Name}\", Intro={track.LoadedIntro.length:N4}, Loop={track.LoadedLoop.length:N4}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1907,7 +1707,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
float timeSinceStartOfLoop = time - offset;
|
float timeSinceStartOfLoop = time - offset;
|
||||||
|
|
||||||
var adjustedTimeNormalized = (LoopLength <= 0f) ? 0f : timeSinceStartOfLoop / LoopLength;
|
var adjustedTimeNormalized = timeSinceStartOfLoop / LoopLength;
|
||||||
|
|
||||||
var beat = adjustedTimeNormalized * Beats;
|
var beat = adjustedTimeNormalized * Beats;
|
||||||
|
|
||||||
|
|
@ -1917,10 +1717,11 @@ namespace MuzikaGromche
|
||||||
IsLooping |= timestamp.IsLooping;
|
IsLooping |= timestamp.IsLooping;
|
||||||
|
|
||||||
#if DEBUG && false
|
#if DEBUG && false
|
||||||
Plugin.Log.LogDebug(string.Format("t={0,10:N4} d={1,7:N4} {2} Time={3:N4} norm={4,6:N4} beat={5,7:N4}",
|
Debug.LogFormat("{0} t={1,10:N4} d={2,7:N4} {3} Time={4:N4} norm={5,6:N4} beat={6,7:N4}",
|
||||||
|
nameof(MuzikaGromche),
|
||||||
Time.realtimeSinceStartup, Time.deltaTime,
|
Time.realtimeSinceStartup, Time.deltaTime,
|
||||||
isExtrapolated ? 'E' : '_', time,
|
isExtrapolated ? 'E' : '_', time,
|
||||||
adjustedTimeNormalized, beat));
|
adjustedTimeNormalized, beat);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return timestamp;
|
return timestamp;
|
||||||
|
|
@ -1955,10 +1756,9 @@ namespace MuzikaGromche
|
||||||
LyricsRandomPerLoop = LyricsRandom.Next();
|
LyricsRandomPerLoop = LyricsRandom.Next();
|
||||||
}
|
}
|
||||||
this.track = track;
|
this.track = track;
|
||||||
AudioState = new(track.LoadedIntro?.length ?? 0f);
|
AudioState = new(track.LoadedIntro.length);
|
||||||
var loadedLoopLength = track.LoadedLoop?.length ?? 0f;
|
WindUpLoopingState = new(track.WindUpTimer, track.LoadedLoop.length, track.Beats);
|
||||||
WindUpLoopingState = new(track.WindUpTimer, loadedLoopLength, track.Beats);
|
LoopLoopingState = new(track.WindUpTimer + track.LoopOffsetInSeconds, track.LoadedLoop.length, track.Beats);
|
||||||
LoopLoopingState = new(track.WindUpTimer + track.LoopOffsetInSeconds, loadedLoopLength, track.Beats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BaseEvent> Update(AudioSource intro, AudioSource loop)
|
public List<BaseEvent> Update(AudioSource intro, AudioSource loop)
|
||||||
|
|
@ -1983,7 +1783,7 @@ namespace MuzikaGromche
|
||||||
LastKnownLoopOffsetBeat = loopOffsetTimestamp.Beat;
|
LastKnownLoopOffsetBeat = loopOffsetTimestamp.Beat;
|
||||||
var events = GetEvents(loopOffsetTimestamp, loopOffsetSpan, windUpOffsetTimestamp);
|
var events = GetEvents(loopOffsetTimestamp, loopOffsetSpan, windUpOffsetTimestamp);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Plugin.Log.LogDebug($"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
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
@ -2042,8 +1842,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
if (GetInterpolation(loopOffsetTimestamp, track.DrunknessLoopOffsetTimeSeries, Easing.Linear) is { } drunkness)
|
if (GetInterpolation(loopOffsetTimestamp, track.DrunknessLoopOffsetTimeSeries, Easing.Linear) is { } drunkness)
|
||||||
{
|
{
|
||||||
var value = Config.ReduceVFXIntensity.Value ? drunkness * 0.3f : drunkness;
|
events.Add(new DrunkEvent(drunkness));
|
||||||
events.Add(new DrunkEvent(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetInterpolation(loopOffsetTimestamp, track.CondensationLoopOffsetTimeSeries, Easing.Linear) is { } condensation)
|
if (GetInterpolation(loopOffsetTimestamp, track.CondensationLoopOffsetTimeSeries, Easing.Linear) is { } condensation)
|
||||||
|
|
@ -2434,8 +2233,6 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
public static ConfigEntry<bool> DisplayLyrics { get; private set; } = null!;
|
public static ConfigEntry<bool> DisplayLyrics { get; private set; } = null!;
|
||||||
|
|
||||||
public static ConfigEntry<bool> ReduceVFXIntensity { get; private set; } = null!;
|
|
||||||
|
|
||||||
public static ConfigEntry<float> AudioOffset { get; private set; } = null!;
|
public static ConfigEntry<float> AudioOffset { get; private set; } = null!;
|
||||||
|
|
||||||
public static ConfigEntry<bool> SkipExplicitTracks { get; private set; } = null!;
|
public static ConfigEntry<bool> SkipExplicitTracks { get; private set; } = null!;
|
||||||
|
|
@ -2502,10 +2299,6 @@ namespace MuzikaGromche
|
||||||
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."));
|
||||||
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(DisplayLyrics, requiresRestart: false));
|
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(DisplayLyrics, requiresRestart: false));
|
||||||
|
|
||||||
ReduceVFXIntensity = configFile.Bind("General", "Reduce Visual Effects", false,
|
|
||||||
new ConfigDescription("Reduce intensity of certain visual effects when you hear the music."));
|
|
||||||
LethalConfigManager.AddConfigItem(new BoolCheckBoxConfigItem(ReduceVFXIntensity, requiresRestart: false));
|
|
||||||
|
|
||||||
Volume = configFile.Bind("General", "Volume", VolumeDefault,
|
Volume = configFile.Bind("General", "Volume", VolumeDefault,
|
||||||
new ConfigDescription("Volume of music played by this mod.", new AcceptableValueRange<float>(VolumeMin, VolumeMax)));
|
new ConfigDescription("Volume of music played by this mod.", new AcceptableValueRange<float>(VolumeMin, VolumeMax)));
|
||||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(Volume, requiresRestart: false));
|
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(Volume, requiresRestart: false));
|
||||||
|
|
@ -2561,9 +2354,8 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create slider entry for track
|
// Create slider entry for track
|
||||||
var seasonal = track.Season is Season season ? $"This is seasonal content for {season.Name}.\n\n" : "";
|
|
||||||
string warning = track.IsExplicit ? "Explicit Content/Lyrics!\n\n" : "";
|
string warning = track.IsExplicit ? "Explicit Content/Lyrics!\n\n" : "";
|
||||||
string description = $"Language: {language.Full}\n\n{seasonal}{warning}Random (relative) chance of selecting this track.\n\nSet to zero to effectively disable the track.";
|
string description = $"Language: {language.Full}\n\n{warning}Random (relative) chance of selecting this track.\n\nSet to zero to effectively disable the track.";
|
||||||
track.Weight = configFile.Bind(
|
track.Weight = configFile.Bind(
|
||||||
new ConfigDefinition(section, track.Name),
|
new ConfigDefinition(section, track.Name),
|
||||||
50,
|
50,
|
||||||
|
|
@ -2819,7 +2611,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogError($"Unable to parse time series: {e}");
|
Debug.Log($"{nameof(MuzikaGromche)} Unable to parse time series: {e}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2836,7 +2628,7 @@ namespace MuzikaGromche
|
||||||
strings.Append(", ");
|
strings.Append(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Plugin.Log.LogDebug($"format time series {ts} {strings}");
|
Debug.Log($"{nameof(MuzikaGromche)} format time series {ts} {strings}");
|
||||||
return strings.ToString();
|
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
|
||||||
|
|
@ -2849,7 +2641,7 @@ namespace MuzikaGromche
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogError($"Unable to parse array: {e}");
|
Debug.Log($"{nameof(MuzikaGromche)} Unable to parse array: {e}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2939,12 +2731,12 @@ namespace MuzikaGromche
|
||||||
.FirstOrDefault(prefab => prefab.Prefab.name == JesterEnemyPrefabName);
|
.FirstOrDefault(prefab => prefab.Prefab.name == JesterEnemyPrefabName);
|
||||||
if (networkPrefab == null)
|
if (networkPrefab == null)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogError("JesterEnemy prefab not found!");
|
Debug.LogError($"{nameof(MuzikaGromche)} JesterEnemy prefab not found!");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkPrefab.Prefab.AddComponent<MuzikaGromcheJesterNetworkBehaviour>();
|
networkPrefab.Prefab.AddComponent<MuzikaGromcheJesterNetworkBehaviour>();
|
||||||
Plugin.Log.LogInfo("Patched JesterEnemy");
|
Debug.Log($"{nameof(MuzikaGromche)} Patched JesterEnemy");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2969,7 +2761,7 @@ namespace MuzikaGromche
|
||||||
var farAudioTransform = gameObject.transform.Find("FarAudio");
|
var farAudioTransform = gameObject.transform.Find("FarAudio");
|
||||||
if (farAudioTransform == null)
|
if (farAudioTransform == null)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogError("JesterEnemy->FarAudio prefab not found!");
|
Debug.LogError($"{nameof(MuzikaGromche)} JesterEnemy->FarAudio prefab not found!");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -2996,7 +2788,7 @@ namespace MuzikaGromche
|
||||||
|
|
||||||
Config.Volume.SettingChanged += UpdateVolume;
|
Config.Volume.SettingChanged += UpdateVolume;
|
||||||
|
|
||||||
Plugin.Log.LogInfo($"{nameof(MuzikaGromcheJesterNetworkBehaviour)} Patched JesterEnemy");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(MuzikaGromcheJesterNetworkBehaviour)} Patched JesterEnemy");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3064,10 +2856,9 @@ namespace MuzikaGromche
|
||||||
[ClientRpc]
|
[ClientRpc]
|
||||||
public void SetTrackClientRpc(string name)
|
public void SetTrackClientRpc(string name)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogInfo($"SetTrackClientRpc {name}");
|
Debug.Log($"{nameof(MuzikaGromche)} SetTrackClientRpc {name}");
|
||||||
if (Plugin.FindTrackNamed(name) is { } track)
|
if (Plugin.FindTrackNamed(name) is { } track)
|
||||||
{
|
{
|
||||||
AudioClipsCacheManager.LoadAudioTrack(track);
|
|
||||||
CurrentTrack = Config.OverrideCurrentTrack(track);
|
CurrentTrack = Config.OverrideCurrentTrack(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3077,7 +2868,7 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
var selectableTrack = Plugin.ChooseTrack();
|
var selectableTrack = Plugin.ChooseTrack();
|
||||||
var audioTrack = selectableTrack.SelectTrack(SelectedTrackIndex);
|
var audioTrack = selectableTrack.SelectTrack(SelectedTrackIndex);
|
||||||
Plugin.Log.LogInfo($"ChooseTrackServerRpc {selectableTrack.Name} #{SelectedTrackIndex} {audioTrack.Name}");
|
Debug.Log($"{nameof(MuzikaGromche)} ChooseTrackServerRpc {selectableTrack.Name} #{SelectedTrackIndex} {audioTrack.Name}");
|
||||||
SetTrackClientRpc(audioTrack.Name);
|
SetTrackClientRpc(audioTrack.Name);
|
||||||
SelectedTrackIndex += 1;
|
SelectedTrackIndex += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -3086,7 +2877,7 @@ namespace MuzikaGromche
|
||||||
{
|
{
|
||||||
double loopStartDspTime = AudioSettings.dspTime + IntroAudioSource.clip.length - IntroAudioSource.time;
|
double loopStartDspTime = AudioSettings.dspTime + IntroAudioSource.clip.length - IntroAudioSource.time;
|
||||||
LoopAudioSource.PlayScheduled(loopStartDspTime);
|
LoopAudioSource.PlayScheduled(loopStartDspTime);
|
||||||
Plugin.Log.LogDebug($"Play Intro: dspTime={AudioSettings.dspTime:N4}, intro.time={IntroAudioSource.time:N4}/{IntroAudioSource.clip.length:N4}, scheduled Loop={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()
|
public void OverrideDeathScreenGameOverText()
|
||||||
|
|
@ -3144,21 +2935,10 @@ namespace MuzikaGromche
|
||||||
var introAudioSource = behaviour.IntroAudioSource;
|
var introAudioSource = behaviour.IntroAudioSource;
|
||||||
var loopAudioSource = behaviour.LoopAudioSource;
|
var loopAudioSource = behaviour.LoopAudioSource;
|
||||||
|
|
||||||
if (behaviour.CurrentTrack == null || behaviour.CurrentTrack.LoadedIntro == null || behaviour.CurrentTrack.LoadedLoop == null)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
if (behaviour.CurrentTrack == null)
|
if (behaviour.CurrentTrack == null)
|
||||||
{
|
{
|
||||||
Plugin.Log.LogError("CurrentTrack is not set!");
|
#if DEBUG
|
||||||
}
|
Debug.LogError($"{nameof(MuzikaGromche)} CurrentTrack is not set!");
|
||||||
else if (AudioClipsCacheManager.AllDone)
|
|
||||||
{
|
|
||||||
Plugin.Log.LogError("Failed to load audio clips, no in-flight requests running");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Plugin.Log.LogDebug($"Waiting for audio clips to load");
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ namespace MuzikaGromche
|
||||||
patch.ManualPatch?.Invoke(animationContainer);
|
patch.ManualPatch?.Invoke(animationContainer);
|
||||||
|
|
||||||
animator.runtimeAnimatorController = patch.AnimatorController;
|
animator.runtimeAnimatorController = patch.AnimatorController;
|
||||||
Plugin.Log.LogDebug($"{nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +223,7 @@ namespace MuzikaGromche
|
||||||
#pragma warning restore CS0162 // Unreachable code detected
|
#pragma warning restore CS0162 // Unreachable code detected
|
||||||
}
|
}
|
||||||
targetObject.name = newName;
|
targetObject.name = newName;
|
||||||
Plugin.Log.LogDebug($"{nameof(PoweredLightsAnimatorsPatch)} {animatorContainer.name}/{relativePath}: Renamed GameObject");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {animatorContainer.name}/{relativePath}: Renamed GameObject");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MuzikaGromche;
|
|
||||||
|
|
||||||
public delegate bool SeasonalContentPredicate(DateTime dateTime);
|
|
||||||
|
|
||||||
// I'm not really sure what to do with seasonal content yet.
|
|
||||||
//
|
|
||||||
// There could be two approaches:
|
|
||||||
// - Force seasonal content tracks to be the only available tracks during their season.
|
|
||||||
// Then seasons must be short, so they don't cut off too much content for too long.
|
|
||||||
// - Exclude seasonal content tracks from the pool when their season is not active.
|
|
||||||
// Considering how many tracks are there in the playlist permanently already,
|
|
||||||
// this might not give the seasonal content enough visibility.
|
|
||||||
//
|
|
||||||
// Either way, seasonal content tracks would be listed in the config UI at all times,
|
|
||||||
// which makes it confusing if you try to select only seasonal tracks outside of their season.
|
|
||||||
|
|
||||||
// Seasons may NOT overlap. There is at most ONE active season at any given date.
|
|
||||||
public readonly record struct Season(string Name, string Description, SeasonalContentPredicate IsActive)
|
|
||||||
{
|
|
||||||
public override string ToString() => Name;
|
|
||||||
|
|
||||||
public static readonly Season NewYear = new("New Year", "New Year and Christmas holiday season", dateTime =>
|
|
||||||
{
|
|
||||||
// December 10 - February 29
|
|
||||||
var month = dateTime.Month;
|
|
||||||
var day = dateTime.Day;
|
|
||||||
return (month == 12 && day >= 10) || (month == 1) || (month == 2 && day <= 29);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Note: it is important that this property goes last
|
|
||||||
public static readonly Season[] All = [NewYear];
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ISeasonalContent
|
|
||||||
{
|
|
||||||
public Season? Season { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SeasonalContentManager
|
|
||||||
{
|
|
||||||
public static Season? CurrentSeason(DateTime dateTime)
|
|
||||||
{
|
|
||||||
foreach (var season in Season.All)
|
|
||||||
{
|
|
||||||
if (season.IsActive(dateTime))
|
|
||||||
{
|
|
||||||
return season;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Season? CurrentSeason() => CurrentSeason(DateTime.Today);
|
|
||||||
|
|
||||||
// Take second approach: filter out seasonal content that is not in the current season.
|
|
||||||
public static IEnumerable<T> Filter<T>(this IEnumerable<T> items, Season? season) where T : ISeasonalContent
|
|
||||||
{
|
|
||||||
return items.Where(item =>
|
|
||||||
{
|
|
||||||
if (item.Season == null)
|
|
||||||
{
|
|
||||||
return true; // always available
|
|
||||||
}
|
|
||||||
return item.Season == season;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> Filter<T>(this IEnumerable<T> items, DateTime dateTime) where T : ISeasonalContent
|
|
||||||
{
|
|
||||||
var season = CurrentSeason(dateTime);
|
|
||||||
return Filter(items, season);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> Filter<T>(this IEnumerable<T> items) where T : ISeasonalContent
|
|
||||||
{
|
|
||||||
return Filter(items, DateTime.Today);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -80,7 +80,7 @@ namespace MuzikaGromche
|
||||||
var multiplier = Mathf.Lerp(minMultiplier, maxMultiplier, normalizedMultiplierTime);
|
var multiplier = Mathf.Lerp(minMultiplier, maxMultiplier, normalizedMultiplierTime);
|
||||||
|
|
||||||
var newWeight = Mathf.FloorToInt(weights[index] * multiplier);
|
var newWeight = Mathf.FloorToInt(weights[index] * multiplier);
|
||||||
Plugin.Log.LogInfo($"{nameof(SpawnRatePatch)} Overriding spawn weight[{index}] {weights[index]} * {multiplier} => {newWeight} for t={SpawnTime}");
|
Debug.Log($"{nameof(MuzikaGromche)} {nameof(SpawnRatePatch)} Overriding spawn weight[{index}] {weights[index]} * {multiplier} => {newWeight} for t={SpawnTime}");
|
||||||
weights[index] = newWeight;
|
weights[index] = newWeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -23,18 +23,12 @@ Muzika Gromche v1337.9001.0 has been updated to work with Lethal Company v73. Pr
|
||||||
|
|
||||||
## Playlist
|
## Playlist
|
||||||
|
|
||||||
English playlist features artists such as **Imagine Dragons, Fall Out Boy, Bon Jovi, Nirvana, Black Eyed Peas, LMFAO** (Party Rock Anthem / Every day I'm shufflin'), **CYBEЯIA** / "Cyberia" (Russian Hackers), **t.A.T.u.**, and of course **Whistle** by Joel Merry / Flo Rida.
|
English playlist features artists such as **Imagine Dragons, Fall Out Boy, Bon Jovi, Black Eyed Peas, LMFAO** (Party Rock Anthem / Every day I'm shufflin'), **CYBEЯIA** / "Cyberia" (Russian Hackers), and of course **Whistle** by Joel Merry / Flo Rida.
|
||||||
|
|
||||||
Russian playlist includes **Би-2, Витас, Глюк’oZa** (Глюкоза) & **Ленинград, Дискотека Авария, Noize MC, Oxxxymiron, Сплин, Пошлая Молли.**
|
Russian playlist includes **Би-2, Витас, Глюк’oZa** (Глюкоза) & **Ленинград, Дискотека Авария, Noize MC, Oxxxymiron, Сплин, Пошлая Молли.**
|
||||||
|
|
||||||
There are also a K-pop track by **aespa**, an anime opening from **One Punch Man,** and an Indian banger by **CarryMinati & Wily Frenzy.**
|
There are also a K-pop track by **aespa**, an anime opening from **One Punch Man,** and an Indian banger by **CarryMinati & Wily Frenzy.**
|
||||||
|
|
||||||
Seasonal New Year's songs:
|
|
||||||
|
|
||||||
- **My Chemical Romance - All I Want for Christmas Is You** (codenamed **IkWilJe**)
|
|
||||||
- **Элизиум - Три белых коня** (codenamed **Paarden**)
|
|
||||||
- **Дискотека Авария - Новогодняя** (codenamed **DiscoKapot**)
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Configuration integrates with [`LethalConfig`] mod.
|
Configuration integrates with [`LethalConfig`] mod.
|
||||||
|
|
@ -58,8 +52,6 @@ Any player can change the following personal preferences locally.
|
||||||
|
|
||||||
See also [mod's release thread](https://discord.com/channels/1168655651455639582/1433881654866477318) at [Lethal Company Modding](https://discord.gg/XeyYqRdRGC) Discord server (in case the invite link expires, there should be a fresh one at [lethal.wiki](https://lethal.wiki/)).
|
See also [mod's release thread](https://discord.com/channels/1168655651455639582/1433881654866477318) at [Lethal Company Modding](https://discord.gg/XeyYqRdRGC) Discord server (in case the invite link expires, there should be a fresh one at [lethal.wiki](https://lethal.wiki/)).
|
||||||
|
|
||||||
Check out my other mod, [HookahPlace ship 'furniture'](https://thunderstore.io/c/lethal-company/p/Ratijas/HookahPlace/)!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
1. Actually not limited to Inverse teleporter or Titan.
|
1. Actually not limited to Inverse teleporter or Titan.
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,7 @@
|
||||||
"version": "4.4.2",
|
"version": "4.4.2",
|
||||||
"commands": [
|
"commands": [
|
||||||
"netcode-patch"
|
"netcode-patch"
|
||||||
],
|
]
|
||||||
"rollForward": false
|
|
||||||
},
|
|
||||||
"tcli": {
|
|
||||||
"version": "0.2.4",
|
|
||||||
"commands": [
|
|
||||||
"tcli"
|
|
||||||
],
|
|
||||||
"rollForward": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "MuzikaGromche",
|
"name": "MuzikaGromche",
|
||||||
"version_number": "1337.9001.4",
|
"version_number": "1337.9001.2",
|
||||||
"author": "Ratijas",
|
"author": "Ratijas",
|
||||||
"description": "Add some content to your inverse teleporter experience on Titan!",
|
"description": "Add some content to your inverse teleporter experience on Titan!",
|
||||||
"website_url": "https://git.vilunov.me/ratijas/muzika-gromche",
|
"website_url": "https://git.vilunov.me/ratijas/muzika-gromche",
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# - set token variable from .env file
|
|
||||||
# - dotnet tool restore
|
|
||||||
# - dotnet tcli publish --file dist/MuzikaGromche-Release.zip
|
|
||||||
|
|
||||||
[config]
|
|
||||||
schemaVersion = "0.0.1"
|
|
||||||
|
|
||||||
[package]
|
|
||||||
namespace = "Ratijas"
|
|
||||||
name = "MuzikaGromche"
|
|
||||||
description = "Add some content to your inverse teleporter experience on Titan!"
|
|
||||||
websiteUrl = "https://git.vilunov.me/ratijas/muzika-gromche"
|
|
||||||
containsNsfwContent = false
|
|
||||||
|
|
||||||
[publish]
|
|
||||||
repository = "https://thunderstore.io"
|
|
||||||
communities = [ "lethal-company" ]
|
|
||||||
|
|
||||||
[publish.categories]
|
|
||||||
lethal-company = [ "mods", "audio", "bepinex", "clientside", "serverside", "monsters" ]
|
|
||||||
Loading…
Reference in New Issue