1
0
Fork 0

Compare commits

..

No commits in common. "work/r/en_US" and "master" have entirely different histories.

16 changed files with 134 additions and 227 deletions

3
.gitignore vendored
View File

@ -5,7 +5,4 @@ riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
.idea/ .idea/
*.dll *.dll
.vs/
dist/
MuzikaGromche.sln.DotSettings.user MuzikaGromche.sln.DotSettings.user
MuzikaGromche.zip

View File

@ -1,3 +1 @@
*.mp3 filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text

BIN
Assets/GodModeLoop.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/GodModeStart.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/MuzikaGromcheLoop.mp3 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Assets/MuzikaGromcheLoop.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/MuzikaGromcheStart.mp3 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Assets/MuzikaGromcheStart.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/PeretasovkaLoop.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/PeretasovkaStart.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/RiseAndShineLoop.ogg (Stored with Git LFS)

Binary file not shown.

BIN
Assets/RiseAndShineStart.ogg (Stored with Git LFS)

Binary file not shown.

View File

@ -10,21 +10,20 @@ using UnityEngine.Networking;
namespace MuzikaGromche namespace MuzikaGromche
{ {
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
public static Track[] Tracks = [ public static Track[] Tracks = [
new Track new Track
{ {
Name = "MuzikaGromche", Name = "MuzikaGromche",
WindUpTimer = 46.3f, WindUpTimer = 46.3f,
Bpm = 130f, Bpm = 130f,
AudioType = AudioType.OGGVORBIS,
}, },
new Track new Track
{ {
Name = "VseVZale", Name = "VseVZale",
WindUpTimer = 39f, WindUpTimer = 39f,
Bpm = 138f, Bpm = 138f,
}, },
new Track new Track
@ -50,89 +49,20 @@ namespace MuzikaGromche
Name = "Durochka", Name = "Durochka",
WindUpTimer = 37f, WindUpTimer = 37f,
Bpm = 130f, Bpm = 130f,
}, }
new Track
{
Name = "GodMode",
WindUpTimer = 40.38f,
Bpm = 108f,
AudioType = AudioType.OGGVORBIS,
},
new Track
{
Name = "RiseAndShine",
WindUpTimer = 59.87f,
Bpm = 137.36f,
AudioType = AudioType.OGGVORBIS,
},
new Track
{
Name = "Peretasovka",
WindUpTimer = 59.04f,
Bpm = 130f,
AudioType = AudioType.OGGVORBIS,
},
]; ];
public static Coroutine JesterLightSwitching; public static Coroutine JesterLightSwitching;
public static Track CurrentTrack; public static Track CurrentTrack;
public static void StartLightSwitching(MonoBehaviour __instance)
{
StopLightSwitching(__instance);
JesterLightSwitching = __instance.StartCoroutine(rotateColors());
}
public static void StopLightSwitching(MonoBehaviour __instance)
{
if (JesterLightSwitching != null) {
__instance.StopCoroutine(JesterLightSwitching);
JesterLightSwitching = null;
}
}
public static void SetLightColor(Color color)
{
foreach (var light in RoundManager.Instance.allPoweredLights)
{
light.color = color;
}
}
public static void ResetLightColor()
{
SetLightColor(Color.white);
}
// TODO: Move to Track class to make them customizable per-song
static List<Color> colors = [Color.magenta, Color.cyan, Color.green, Color.yellow];
public static IEnumerator rotateColors()
{
Debug.Log("Starting color rotation");
var i = 0;
while (true)
{
var color = colors[i];
Debug.Log("Chose color " + color);
SetLightColor(color);
i = (i + 1) % colors.Count;
if (CurrentTrack != null) {
yield return new WaitForSeconds(60f / CurrentTrack.Bpm);
} else {
yield break;
}
}
}
private void Awake() private void Awake()
{ {
string text = Info.Location.TrimEnd((PluginInfo.PLUGIN_NAME + ".dll").ToCharArray()); string text = Info.Location.TrimEnd((PluginInfo.PLUGIN_NAME + ".dll").ToCharArray());
UnityWebRequest[] requests = new UnityWebRequest[Tracks.Length * 2]; UnityWebRequest[] requests = new UnityWebRequest[Tracks.Length * 2];
for (int i = 0; i < Tracks.Length; i++) { for (int i = 0; i < Tracks.Length; i++) {
Track track = Tracks[i]; Track track = Tracks[i];
requests[i * 2] = UnityWebRequestMultimedia.GetAudioClip($"File://{text}{track.FileNameStart}", track.AudioType); requests[i * 2] = UnityWebRequestMultimedia.GetAudioClip($"File://{text}{track.Name}Start.mp3", AudioType.MPEG);
requests[i * 2 + 1] = UnityWebRequestMultimedia.GetAudioClip($"File://{text}{track.FileNameLoop}", track.AudioType); requests[i * 2 + 1] = UnityWebRequestMultimedia.GetAudioClip($"File://{text}{track.Name}Loop.mp3", AudioType.MPEG);
requests[i * 2].SendWebRequest(); requests[i * 2].SendWebRequest();
requests[i * 2 + 1].SendWebRequest(); requests[i * 2 + 1].SendWebRequest();
} }
@ -141,130 +71,141 @@ namespace MuzikaGromche
if (requests.All(request => request.result == UnityWebRequest.Result.Success)) { if (requests.All(request => request.result == UnityWebRequest.Result.Success)) {
for (int i = 0; i < Tracks.Length; i++) { for (int i = 0; i < Tracks.Length; i++) {
Track track = Tracks[i]; Tracks[i].LoadedStart = DownloadHandlerAudioClip.GetContent(requests[i * 2]);
track.LoadedStart = DownloadHandlerAudioClip.GetContent(requests[i * 2]); Tracks[i].LoadedLoop = DownloadHandlerAudioClip.GetContent(requests[i * 2 + 1]);
track.LoadedLoop = DownloadHandlerAudioClip.GetContent(requests[i * 2 + 1]);
} }
new Harmony(PluginInfo.PLUGIN_NAME).PatchAll(typeof(JesterPatch)); new Harmony(PluginInfo.PLUGIN_NAME).PatchAll(typeof(JesterPatch));
} else { } else {
Logger.LogError("Could not load audio file"); Logger.LogError("Could not load audio file");
} }
} }
} }
public class Track public class Track
{ {
public string Name; public string Name;
// Wind-up time can be shorter than the Start audio track, so that public float WindUpTimer;
// the "pop" effect can be baked in the Start audio and kept away public float Bpm;
// from the looped part. public AudioClip LoadedStart;
public float WindUpTimer; public AudioClip LoadedLoop;
// BPM for light switching in sync with the music. There is no offset, }
// so the Loop track should start precisely on a beat.
public float Bpm;
// MPEG is basically mp3, and it can produce gaps at the start. [HarmonyPatch(typeof(JesterAI))]
// WAV is OK, but takes a lot of space. Try OGGVORBIS instead. internal class JesterPatch
public AudioType AudioType = AudioType.MPEG; {
[HarmonyPatch("Update")]
[HarmonyPrefix]
public static void DoNotStopTheMusicPrefix(JesterAI __instance, out State __state)
{
__state = new State();
__state.prevStateindex = __instance.previousState;
if (__instance.currentBehaviourStateIndex == 2 && __instance.previousBehaviourStateIndex != 2) {
// if just popped out
// then override farAudio so that vanilla logic does not stop the music
__state.farAudio = __instance.farAudio;
__instance.farAudio = __instance.creatureVoice;
}
}
static List<Color> colors = [Color.magenta, Color.cyan, Color.green, Color.yellow];
public AudioClip LoadedStart; public static IEnumerator rotateColors()
public AudioClip LoadedLoop; {
Debug.Log("Starting color rotation");
var i = 0;
while (true)
{
var color = colors[i];
Debug.Log("Chose color " + color);
foreach (var light in RoundManager.Instance.allPoweredLights)
{
light.color = color;
}
public string FileNameStart => $"{Name}Start.{ext}"; i += 1;
public string FileNameLoop => $"{Name}Loop.{ext}"; if (i >= colors.Count) i = 0;
private string ext => AudioType switch yield return new WaitForSeconds(60f / Plugin.CurrentTrack.Bpm);
{ }
AudioType.MPEG => "mp3", }
AudioType.WAV => "wav",
AudioType.OGGVORBIS => "ogg",
_ => "",
};
}
[HarmonyPatch(typeof(JesterAI))]
internal class JesterPatch
{
[HarmonyPatch("Update")]
[HarmonyPrefix]
public static void DoNotStopTheMusicPrefix(JesterAI __instance, out State __state)
{
__state = new State();
__state.prevStateindex = __instance.previousState;
if (__instance.currentBehaviourStateIndex == 2 && __instance.previousBehaviourStateIndex != 2) {
// if just popped out
// then override farAudio so that vanilla logic does not stop the music
__state.farAudio = __instance.farAudio;
__instance.farAudio = __instance.creatureVoice;
}
}
[HarmonyPatch("Update")] [HarmonyPatch("Update")]
[HarmonyPostfix] [HarmonyPostfix]
public static void DoNotStopTheMusic(JesterAI __instance, State __state) public static void DoNotStopTheMusic(JesterAI __instance, State __state)
{ {
if (__state.farAudio != null) if (__state.farAudio != null)
{ {
__instance.farAudio = __state.farAudio; __instance.farAudio = __state.farAudio;
} }
if (__instance.currentBehaviourStateIndex is 1 && __state.prevStateindex != 1)
{
// if just started winding up
// then stop the default music...
__instance.farAudio.Stop();
__instance.creatureVoice.Stop();
// ...and start modded music
var seed = RoundManager.Instance.dungeonGenerator.Generator.ChosenSeed;
var sha = SHA256.Create();
var hash = sha.ComputeHash(BitConverter.GetBytes(seed));
var trackId = 0;
foreach (var t in hash)
{
// modulus division on byte array
trackId *= 256 % Plugin.Tracks.Length;
trackId %= Plugin.Tracks.Length;
trackId += t % Plugin.Tracks.Length;
trackId %= Plugin.Tracks.Length;
}
Debug.Log($"Seed is {seed}, chosen track is {trackId} out of {Plugin.Tracks.Length} tracks");
Plugin.CurrentTrack = Plugin.Tracks[trackId];
__instance.popUpTimer = Plugin.CurrentTrack.WindUpTimer;
__instance.farAudio.maxDistance = 150;
__instance.farAudio.clip = Plugin.CurrentTrack.LoadedStart;
__instance.farAudio.loop = false;
Debug.Log($"Playing start music: maxDistance: {__instance.farAudio.maxDistance}, minDistance: {__instance.farAudio.minDistance}, volume: {__instance.farAudio.volume}, spread: {__instance.farAudio.spread}");
__instance.farAudio.Play();
}
if (__instance.currentBehaviourStateIndex is 1 && __state.prevStateindex != 1) if (__instance.currentBehaviourStateIndex is 2 && __state.prevStateindex != 2)
{ {
// if just started winding up __instance.creatureVoice.Stop();
// then stop the default music...
__instance.farAudio.Stop(); if (Plugin.JesterLightSwitching != null) {
__instance.creatureVoice.Stop(); __instance.StopCoroutine(Plugin.JesterLightSwitching);
Plugin.JesterLightSwitching = null;
}
Plugin.JesterLightSwitching = __instance.StartCoroutine(rotateColors());
}
// ...and start modded music if (__instance.currentBehaviourStateIndex != 2 && __state.prevStateindex == 2)
var seed = RoundManager.Instance.dungeonGenerator.Generator.ChosenSeed; {
var sha = SHA256.Create(); if (Plugin.JesterLightSwitching != null) {
var hash = sha.ComputeHash(BitConverter.GetBytes(seed)); __instance.StopCoroutine(Plugin.JesterLightSwitching);
var trackId = 0; Plugin.JesterLightSwitching = null;
foreach (var t in hash) }
{ foreach (var light in RoundManager.Instance.allPoweredLights)
// modulus division on byte array {
trackId *= 256 % Plugin.Tracks.Length; light.color = Color.white;
trackId %= Plugin.Tracks.Length; }
trackId += t % Plugin.Tracks.Length; }
trackId %= Plugin.Tracks.Length;
}
Debug.Log($"Seed is {seed}, chosen track is {trackId} out of {Plugin.Tracks.Length} tracks");
Plugin.CurrentTrack = Plugin.Tracks[trackId];
__instance.popUpTimer = Plugin.CurrentTrack.WindUpTimer;
__instance.farAudio.maxDistance = 150;
__instance.farAudio.clip = Plugin.CurrentTrack.LoadedStart;
__instance.farAudio.loop = false;
Debug.Log($"Playing start music: maxDistance: {__instance.farAudio.maxDistance}, minDistance: {__instance.farAudio.minDistance}, volume: {__instance.farAudio.volume}, spread: {__instance.farAudio.spread}");
__instance.farAudio.Play();
}
if (__instance.currentBehaviourStateIndex is 2 && __state.prevStateindex != 2) if (__instance.currentBehaviourStateIndex is 2 && !__instance.creatureVoice.isPlaying)
{ {
__instance.creatureVoice.Stop(); __instance.creatureVoice.maxDistance = 150;
Plugin.StartLightSwitching(__instance); __instance.creatureVoice.clip = Plugin.CurrentTrack.LoadedLoop;
} var time = __instance.farAudio.time;
var delay = Plugin.CurrentTrack.LoadedStart.length - time;
Debug.Log($"Start length: {Plugin.CurrentTrack.LoadedStart.length}; played time: {time}");
Debug.Log($"Playing loop music: maxDistance: {__instance.creatureVoice.maxDistance}, minDistance: {__instance.creatureVoice.minDistance}, volume: {__instance.creatureVoice.volume}, spread: {__instance.creatureVoice.spread}, in seconds: {delay}");
__instance.creatureVoice.PlayDelayed(delay);
}
}
}
if (__instance.currentBehaviourStateIndex != 2 && __state.prevStateindex == 2) internal class State
{ {
Plugin.StopLightSwitching(__instance); public AudioSource farAudio;
Plugin.ResetLightColor(); public int prevStateindex;
} }
if (__instance.currentBehaviourStateIndex is 2 && !__instance.creatureVoice.isPlaying)
{
__instance.creatureVoice.maxDistance = 150;
__instance.creatureVoice.clip = Plugin.CurrentTrack.LoadedLoop;
var time = __instance.farAudio.time;
var delay = Plugin.CurrentTrack.LoadedStart.length - time;
Debug.Log($"Start length: {Plugin.CurrentTrack.LoadedStart.length}; played time: {time}");
Debug.Log($"Playing loop music: maxDistance: {__instance.creatureVoice.maxDistance}, minDistance: {__instance.creatureVoice.minDistance}, volume: {__instance.creatureVoice.volume}, spread: {__instance.creatureVoice.spread}, in seconds: {delay}");
__instance.creatureVoice.PlayDelayed(delay);
}
}
}
internal class State
{
public AudioSource farAudio;
public int prevStateindex;
}
} }

View File

@ -1 +0,0 @@
Adds some content to your reverse teleports on Titan

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

View File

@ -1,10 +0,0 @@
{
"name": "MuzikaGromche",
"version_number": "13.37.6",
"author": "Oflor",
"description": "Glaza zakryvaj",
"website_url": "https://git.vilunov.me/nikita/muzika-gromche",
"dependencies": [
"BepInEx-BepInExPack-5.4.2100"
]
}