diff --git a/Classes/OpusComponent.cs b/Classes/OpusComponent.cs new file mode 100644 index 0000000..35fd534 --- /dev/null +++ b/Classes/OpusComponent.cs @@ -0,0 +1,321 @@ +/**************************************************************************** +* +* NAME: PitchShift.cs +* VERSION: 1.2 +* HOME URL: http://www.dspdimension.com +* KNOWN BUGS: none +* +* SYNOPSIS: Routine for doing pitch shifting while maintaining +* duration using the Short Time Fourier Transform. +* +* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5 +* (one octave down) and 2. (one octave up). A value of exactly 1 does not change +* the pitch. numSampsToProcess tells the routine how many samples in indata[0... +* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ... +* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the +* data in-place). fftFrameSize defines the FFT frame size used for the +* processing. Typical values are 1024, 2048 and 4096. It may be any value <= +* MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT +* oversampling factor which also determines the overlap between adjacent STFT +* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is +* recommended for best quality. sampleRate takes the sample rate for the signal +* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in +* indata[] should be in the range [-1.0, 1.0), which is also the output range +* for the data, make sure you scale the data accordingly (for 16bit signed integers +* you would have to divide (and multiply) by 32768). +* +* COPYRIGHT 1999-2006 Stephan M. Bernsee +* +* The Wide Open License (WOL) +* +* Permission to use, copy, modify, distribute and sell this software and its +* documentation for any purpose is hereby granted without fee, provided that +* the above copyright notice and this license appear in all source copies. +* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF +* ANY KIND. See http://www.dspguru.com/wol.htm for more information. +* +*****************************************************************************/ + +/**************************************************************************** +* +* This code was converted to C# by Michael Knight +* madmik3 at gmail dot com. +* http://sites.google.com/site/mikescoderama/ +* +*****************************************************************************/ + +using SCPSLAudioApi.AudioCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using VoiceChat.Codec; + +namespace SCP294.Classes +{ + public class OpusComponent : MonoBehaviour + { + /// + /// The ReferenceHub instance that this player sends as. + /// + public ReferenceHub Owner { get; set; } + + public OpusEncoder Encoder { get; } = new OpusEncoder(VoiceChat.Codec.Enums.OpusApplicationType.Voip); + public OpusDecoder Decoder { get; } = new OpusDecoder(); + + /// + /// Add or retrieve the OpusComponent instance based on a ReferenceHub instance. + /// + /// The ReferenceHub instance that this OpusComponent belongs to + /// + public static OpusComponent Get(ReferenceHub hub) + { + if (SCP294.Instance.Encoders.TryGetValue(hub, out OpusComponent player)) + { + return player; + } + + player = hub.gameObject.AddComponent(); + player.Owner = hub; + + SCP294.Instance.Encoders.Add(hub, player); + return player; + } + + #region Private Static Memebers + private static int MAX_FRAME_LENGTH = 16000; + private float[] gInFIFO = new float[MAX_FRAME_LENGTH]; + private float[] gOutFIFO = new float[MAX_FRAME_LENGTH]; + private float[] gFFTworksp = new float[2 * MAX_FRAME_LENGTH]; + private float[] gLastPhase = new float[MAX_FRAME_LENGTH / 2 + 1]; + private float[] gSumPhase = new float[MAX_FRAME_LENGTH / 2 + 1]; + private float[] gOutputAccum = new float[2 * MAX_FRAME_LENGTH]; + private float[] gAnaFreq = new float[MAX_FRAME_LENGTH]; + private float[] gAnaMagn = new float[MAX_FRAME_LENGTH]; + private float[] gSynFreq = new float[MAX_FRAME_LENGTH]; + private float[] gSynMagn = new float[MAX_FRAME_LENGTH]; + private long gRover, gInit; + #endregion + + #region Public Static Methods + public void PitchShift(float pitchShift, long numSampsToProcess, + float sampleRate, float[] indata) + { + PitchShift(pitchShift, numSampsToProcess, (long)2048, (long)10, sampleRate, indata); + } + public void PitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize, + long osamp, float sampleRate, float[] indata) + { + double magn, phase, tmp, window, real, imag; + double freqPerBin, expct; + long i, k, qpd, index, inFifoLatency, stepSize, fftFrameSize2; + + + float[] outdata = indata; + /* set up some handy variables */ + fftFrameSize2 = fftFrameSize / 2; + stepSize = fftFrameSize / osamp; + freqPerBin = sampleRate / (double)fftFrameSize; + expct = 2.0 * Math.PI * (double)stepSize / (double)fftFrameSize; + inFifoLatency = fftFrameSize - stepSize; + if (gRover == 0) gRover = inFifoLatency; + + + /* main processing loop */ + for (i = 0; i < numSampsToProcess; i++) + { + + /* As long as we have not yet collected enough data just read in */ + gInFIFO[gRover] = indata[i]; + outdata[i] = gOutFIFO[gRover - inFifoLatency]; + gRover++; + + /* now we have enough data for processing */ + if (gRover >= fftFrameSize) + { + gRover = inFifoLatency; + + /* do windowing and re,im interleave */ + for (k = 0; k < fftFrameSize; k++) + { + window = -.5 * Math.Cos(2.0 * Math.PI * (double)k / (double)fftFrameSize) + .5; + gFFTworksp[2 * k] = (float)(gInFIFO[k] * window); + gFFTworksp[2 * k + 1] = 0.0F; + } + + + /* ***************** ANALYSIS ******************* */ + /* do transform */ + ShortTimeFourierTransform(gFFTworksp, fftFrameSize, -1); + + /* this is the analysis step */ + for (k = 0; k <= fftFrameSize2; k++) + { + + /* de-interlace FFT buffer */ + real = gFFTworksp[2 * k]; + imag = gFFTworksp[2 * k + 1]; + + /* compute magnitude and phase */ + magn = 2.0 * Math.Sqrt(real * real + imag * imag); + phase = Math.Atan2(imag, real); + + /* compute phase difference */ + tmp = phase - gLastPhase[k]; + gLastPhase[k] = (float)phase; + + /* subtract expected phase difference */ + tmp -= (double)k * expct; + + /* map delta phase into +/- Pi interval */ + qpd = (long)(tmp / Math.PI); + if (qpd >= 0) qpd += qpd & 1; + else qpd -= qpd & 1; + tmp -= Math.PI * (double)qpd; + + /* get deviation from bin frequency from the +/- Pi interval */ + tmp = osamp * tmp / (2.0 * Math.PI); + + /* compute the k-th partials' true frequency */ + tmp = (double)k * freqPerBin + tmp * freqPerBin; + + /* store magnitude and true frequency in analysis arrays */ + gAnaMagn[k] = (float)magn; + gAnaFreq[k] = (float)tmp; + + } + + /* ***************** PROCESSING ******************* */ + /* this does the actual pitch shifting */ + for (int zero = 0; zero < fftFrameSize; zero++) + { + gSynMagn[zero] = 0; + gSynFreq[zero] = 0; + } + + for (k = 0; k <= fftFrameSize2; k++) + { + index = (long)(k * pitchShift); + if (index <= fftFrameSize2) + { + gSynMagn[index] += gAnaMagn[k]; + gSynFreq[index] = gAnaFreq[k] * pitchShift; + } + } + + /* ***************** SYNTHESIS ******************* */ + /* this is the synthesis step */ + for (k = 0; k <= fftFrameSize2; k++) + { + + /* get magnitude and true frequency from synthesis arrays */ + magn = gSynMagn[k]; + tmp = gSynFreq[k]; + + /* subtract bin mid frequency */ + tmp -= (double)k * freqPerBin; + + /* get bin deviation from freq deviation */ + tmp /= freqPerBin; + + /* take osamp into account */ + tmp = 2.0 * Math.PI * tmp / osamp; + + /* add the overlap phase advance back in */ + tmp += (double)k * expct; + + /* accumulate delta phase to get bin phase */ + gSumPhase[k] += (float)tmp; + phase = gSumPhase[k]; + + /* get real and imag part and re-interleave */ + gFFTworksp[2 * k] = (float)(magn * Math.Cos(phase)); + gFFTworksp[2 * k + 1] = (float)(magn * Math.Sin(phase)); + } + + /* zero negative frequencies */ + for (k = fftFrameSize + 2; k < 2 * fftFrameSize; k++) gFFTworksp[k] = 0.0F; + + /* do inverse transform */ + ShortTimeFourierTransform(gFFTworksp, fftFrameSize, 1); + + /* do windowing and add to output accumulator */ + for (k = 0; k < fftFrameSize; k++) + { + window = -.5 * Math.Cos(2.0 * Math.PI * (double)k / (double)fftFrameSize) + .5; + gOutputAccum[k] += (float)(2.0 * window * gFFTworksp[2 * k] / (fftFrameSize2 * osamp)); + } + for (k = 0; k < stepSize; k++) gOutFIFO[k] = gOutputAccum[k]; + + /* shift accumulator */ + //memmove(gOutputAccum, gOutputAccum + stepSize, fftFrameSize * sizeof(float)); + for (k = 0; k < fftFrameSize; k++) + { + gOutputAccum[k] = gOutputAccum[k + stepSize]; + } + + /* move input FIFO */ + for (k = 0; k < inFifoLatency; k++) gInFIFO[k] = gInFIFO[k + stepSize]; + } + } + } + #endregion + + #region Private Static Methods + public static void ShortTimeFourierTransform(float[] fftBuffer, long fftFrameSize, long sign) + { + float wr, wi, arg, temp; + float tr, ti, ur, ui; + long i, bitm, j, le, le2, k; + + for (i = 2; i < 2 * fftFrameSize - 2; i += 2) + { + for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) + { + if ((i & bitm) != 0) j++; + j <<= 1; + } + if (i < j) + { + temp = fftBuffer[i]; + fftBuffer[i] = fftBuffer[j]; + fftBuffer[j] = temp; + temp = fftBuffer[i + 1]; + fftBuffer[i + 1] = fftBuffer[j + 1]; + fftBuffer[j + 1] = temp; + } + } + long max = (long)(Math.Log(fftFrameSize) / Math.Log(2.0) + .5); + for (k = 0, le = 2; k < max; k++) + { + le <<= 1; + le2 = le >> 1; + ur = 1.0F; + ui = 0.0F; + arg = (float)Math.PI / (le2 >> 1); + wr = (float)Math.Cos(arg); + wi = (float)(sign * Math.Sin(arg)); + for (j = 0; j < le2; j += 2) + { + + for (i = j; i < 2 * fftFrameSize; i += le) + { + tr = fftBuffer[i + le2] * ur - fftBuffer[i + le2 + 1] * ui; + ti = fftBuffer[i + le2] * ui + fftBuffer[i + le2 + 1] * ur; + fftBuffer[i + le2] = fftBuffer[i] - tr; + fftBuffer[i + le2 + 1] = fftBuffer[i + 1] - ti; + fftBuffer[i] += tr; + fftBuffer[i + 1] += ti; + + } + tr = ur * wr - ui * wi; + ui = ur * wi + ui * wr; + ur = tr; + } + } + } + #endregion + } +} diff --git a/Classes/SoundHandler.cs b/Classes/SoundHandler.cs index adb6d68..38cc1ea 100644 --- a/Classes/SoundHandler.cs +++ b/Classes/SoundHandler.cs @@ -13,12 +13,14 @@ using System.IO; using UnityEngine; using VoiceChat; +using VoiceChat.Codec; using YamlDotNet.Core.Tokens; namespace SCP294 { public class SoundHandler { + public OpusEncoder Encoder = new OpusEncoder(VoiceChat.Codec.Enums.OpusApplicationType.Voip); // Borrowed from AutoEvents <3 public static List AudioPlayers = new List(); diff --git a/Commands/SCP294Command.cs b/Commands/SCP294Command.cs index 0db9ea2..7a10a1f 100644 --- a/Commands/SCP294Command.cs +++ b/Commands/SCP294Command.cs @@ -72,7 +72,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = "This SCP-294 has been deactivated..."; return false; } - if (arguments.Count < 1) + if (arguments.Count < 1 && !SCP294.Instance.Config.ForceRandom) { response = "Requires you hold a coin to work | Usage: .scp294 OR .scp294 player "; return false; @@ -82,7 +82,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = "You aren't holding a coin to buy a drink with."; return false; } - if (arguments.At(0).ToLower() == "player") { + if (arguments.Count > 0 && arguments.At(0).ToLower() == "player") { // Player Cup // Try and Get player Player targetPlayer = Player.Get(String.Join(" ", arguments.Skip(1).ToArray())); @@ -156,7 +156,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s response = "SCP-294 couldn't determine your drink, and refunded you your coin."; return false; } - else if (arguments.At(0).ToLower() == "playercum") + else if (arguments.Count > 0 && arguments.At(0).ToLower() == "playercum") { // Player Cup // Try and Get player @@ -215,6 +215,11 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } else { + if (SCP294.Instance.Config.ForceRandom || arguments.At(0).ToLower() == "random") + { + arguments = new ArraySegment(SCP294.Instance.DrinkManager.LoadedDrinks.RandomItem().DrinkNames.RandomItem().Split()); + } + // Other Drinks foreach (CustomDrink customDrink in SCP294.Instance.DrinkManager.LoadedDrinks) { @@ -262,7 +267,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s SCP294.Instance.SpawnedSCP294s[scp294] = false; }); - response = $"SCP-294 Started Dispensing a Drink of {drinkName}."; + response = $"SCP-294 Started Dispensing a Drink of {drinkName}. {(SCP294.Instance.Config.ForceRandom ? "(Server Forced Random Drink)" : "")}"; SCP294Object.SetSCP294Uses(scp294, SCP294.Instance.SCP294UsesLeft[scp294] - 1); return true; } else @@ -407,7 +412,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s SCP294.Instance.SpawnedSCP294s[scp294] = false; }); - response = $"SCP-294 Started Dispensing a Drink of {drinkName}."; + response = $"SCP-294 Started Dispensing a Drink of {drinkName}. {(SCP294.Instance.Config.ForceRandom ? "(Server Forced Random Drink)" : "")}"; SCP294Object.SetSCP294Uses(scp294, SCP294.Instance.SCP294UsesLeft[scp294] - 1); return true; } diff --git a/Config.cs b/Config.cs index fdb38ea..afc0dd4 100644 --- a/Config.cs +++ b/Config.cs @@ -15,14 +15,17 @@ public class Config : IConfig /// /// Will the plugin run? /// + [Description("Will the plugin run?")] public bool IsEnabled { get; set; } = true; /// /// Will the plugin print Debug Text? /// + [Description("Will the plugin print Debug Text?")] public bool Debug { get; set; } = false; /// /// Configure the Spawning Locations of SCP-294 /// + [Description("Configure the Spawning Locations of SCP-294")] public SpawningConfig SpawningLocations { get; set; } = new SpawningConfig() { SpawnAmount = 1, SpawnRooms = new Dictionary>() { @@ -60,36 +63,54 @@ public class Config : IConfig } }; /// - /// Will the plugin print Debug Text? + /// Enable Voice Effects? Disable this if performance is subpar and you give out more than about 8 drinks per round. + /// + [Description("Enable Voice Effects? Disable this if performance is subpar and you give out more than about 8 drinks per round.")] + public bool EnableVoiceEffects { get; set; } = true; + /// + /// Should players be forced to get a random drink? (Player drinks will still be requestable) + /// + [Description("Should players be forced to get a random drink? (Player drinks will still be requestable)")] + public bool ForceRandom { get; set; } = false; + /// + /// How close to the machine does the player have to be? /// + [Description("How close to the machine does the player have to be?")] public float UseDistance { get; set; } = 2.5f; /// /// Should the Cola be dispensed into the machine's output? Set to False to put it in the player's inventory. /// + [Description("Should the Cola be dispensed into the machine's output? Set to False to put it in the player's inventory.")] public bool SpawnInOutput { get; set; } = true; /// /// How long should it take from command execution to dispense the drink? /// + [Description("How long should it take from command execution to dispense the drink?")] public float DispenseDelay { get; set; } = 5.5f; /// /// Cooldown after a player uses the machine. Starts exactly as the coin is inserted /// + [Description("Cooldown after a player uses the machine. Starts exactly as the coin is inserted")] public float CooldownTime { get; set; } = 10f; /// /// Enable to use the Community Made Drinks /// + [Description("Enable to use the Community Made Drinks")] public bool EnableCommunityDrinks { get; set; } = true; /// /// The maximum uses of a SCP-294 machine before it deactivates. Set to -1 for infinite uses /// + [Description("The maximum uses of a SCP-294 machine before it deactivates. Set to -1 for infinite uses")] public int MaxUsesPerMachine { get; set; } = 3; /// /// The maximum size a player can grow to from a drink. /// + [Description("The maximum size a player can grow to from a drink.")] public Vector3 MaxSizeFromDrink { get; set; } = new Vector3(1.3f,1.3f,1.3f); /// - /// The minimum size a player can grow to from a drink. + /// The minimum size a player can shrink to from a drink. /// + [Description("The minimum size a player can shrink to from a drink.")] public Vector3 MinSizeFromDrink { get; set; } = new Vector3(0.7f,0.7f,0.7f); } } diff --git a/Handlers/playerHandler.cs b/Handlers/playerHandler.cs index 062ef2c..89a0760 100644 --- a/Handlers/playerHandler.cs +++ b/Handlers/playerHandler.cs @@ -16,6 +16,11 @@ namespace SCP294.handlers { public class playerHandler { + public void Joined(JoinedEventArgs args) + { + + } + public void ChangingItem(ChangingItemEventArgs args) { if (args.Player == null) return; if (args.Item == null) return; diff --git a/Handlers/serverHandler.cs b/Handlers/serverHandler.cs index b0efef4..efb520a 100644 --- a/Handlers/serverHandler.cs +++ b/Handlers/serverHandler.cs @@ -1,16 +1,8 @@ -using Exiled.API.Enums; -using Exiled.API.Features; -using Exiled.Events; -using Exiled.Events.EventArgs; -using SCP294.Utils; -using System.Collections.Generic; -using MapEditorReborn.API; -using MapEditorReborn.API.Features; -using MapEditorReborn.Commands.ModifyingCommands.Scale; -using UnityEngine; +using MapEditorReborn.API.Features.Objects; using SCP294.Classes; -using MapEditorReborn.API.Features.Objects; using SCP294.Types; +using System.Collections.Generic; +using VoiceChat.Codec; namespace SCP294.handlers { @@ -20,6 +12,8 @@ public void WaitingForPlayers() { SCP294.Instance.SpawnedSCP294s = new Dictionary(); SCP294.Instance.PlayersNear294 = new List(); SCP294.Instance.CustomDrinkItems = new Dictionary(); + SCP294.Instance.PlayerVoicePitch = new Dictionary(); + SCP294.Instance.Encoders = new Dictionary(); SCP294Object.SpawnSCP294(); } } diff --git a/Patches/OnEffectsApplied.cs b/Patches/OnEffectsApplied.cs index 47fbab4..8944024 100644 --- a/Patches/OnEffectsApplied.cs +++ b/Patches/OnEffectsApplied.cs @@ -1,19 +1,21 @@ -using HarmonyLib; +using Exiled.API.Features; +using HarmonyLib; using InventorySystem.Items; using InventorySystem.Items.Usables; +using Mirror; using NorthwoodLib.Pools; -using PlayerRoles.PlayableScps.Scp096; +using PlayerRoles.Voice; +using SCP294.Classes; using SCP294.Types; -using System; using System.Collections.Generic; -using System.Linq; using System.Reflection.Emit; -using System.Text; -using System.Threading.Tasks; - +using VoiceChat; +using VoiceChat.Codec; +using VoiceChat.Networking; namespace SCP294.Patches { + [HarmonyPatch(typeof(Scp207), nameof(Scp207.OnEffectsActivated))] internal static class SCP207OnEffectsActivated { @@ -67,4 +69,65 @@ static IEnumerable Transpiler(IEnumerable inst ListPool.Shared.Return(newInstructions); } } + + [HarmonyPatch(typeof(VoiceTransceiver), nameof(VoiceTransceiver.ServerReceiveMessage))] + internal static class ServerReceiveMessage + { + [HarmonyPrefix] + private static bool Prefix(NetworkConnection conn, VoiceMessage msg) + { + if (!SCP294.Instance.Config.EnableVoiceEffects) return true; + + if (msg.SpeakerNull || msg.Speaker.netId != conn.identity.netId) + { + return false; + } + IVoiceRole voiceRole = msg.Speaker.roleManager.CurrentRole as IVoiceRole; + if (voiceRole == null) + { + return false; + } + if (!voiceRole.VoiceModule.CheckRateLimit()) + { + return false; + } + if (VoiceChatMutes.IsMuted(msg.Speaker, false)) + { + return false; + } + VoiceChatChannel voiceChatChannel = voiceRole.VoiceModule.ValidateSend(msg.Channel); + if (voiceChatChannel == VoiceChatChannel.None) + { + return false; + } + voiceRole.VoiceModule.CurrentChannel = voiceChatChannel; + + Player plr = Player.Get(msg.Speaker); + if (SCP294.Instance.PlayerVoicePitch.TryGetValue(plr.UserId, out float pitchShift) && pitchShift != 1f) + { + float[] message = new float[48000]; + OpusComponent comp = OpusComponent.Get(plr.ReferenceHub); + comp.Decoder.Decode(msg.Data, msg.DataLength, message); + + comp.PitchShift(pitchShift, (long)480, 48000, message); + + msg.DataLength = comp.Encoder.Encode(message, msg.Data, 480); + } + + foreach (ReferenceHub referenceHub in ReferenceHub.AllHubs) + { + IVoiceRole voiceRole2 = referenceHub.roleManager.CurrentRole as IVoiceRole; + if (voiceRole2 != null) + { + VoiceChatChannel voiceChatChannel2 = voiceRole2.VoiceModule.ValidateReceive(msg.Speaker, voiceChatChannel); + if (voiceChatChannel2 != VoiceChatChannel.None) + { + msg.Channel = voiceChatChannel2; + referenceHub.connectionToClient.Send(msg, 0); + } + } + } + return false; + } + } } diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index c421546..d540a84 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.1.1.0")] +[assembly: AssemblyFileVersion("1.1.1.0")] diff --git a/SCP294.cs b/SCP294.cs index e27359c..4e9248a 100644 --- a/SCP294.cs +++ b/SCP294.cs @@ -9,6 +9,7 @@ using SCP294.Types; using HarmonyLib; using Exiled.API.Enums; +using VoiceChat.Codec; namespace SCP294 { @@ -16,7 +17,7 @@ public class SCP294 : Plugin { public override string Name => "Ultimate294"; public override string Author => "creepycats"; - public override Version Version => new Version(1, 1, 0); + public override Version Version => new Version(1, 1, 1); public override PluginPriority Priority => PluginPriority.Highest; @@ -28,11 +29,14 @@ public class SCP294 : Plugin public List PlayersNear294 { get; set; } = new List(); public Dictionary CustomDrinkItems = new Dictionary(); public DrinkManager DrinkManager = new DrinkManager(); + public Dictionary PlayerVoicePitch = new Dictionary(); private Harmony _harmony; private CoroutineHandle hintCoroutine; + public Dictionary Encoders = new Dictionary(); + public override void OnEnabled() { Instance = this; @@ -82,6 +86,7 @@ public void RegisterEvents() Player.ChangingItem += PlayerHandler.ChangingItem; Player.UsedItem += PlayerHandler.UsedItem; + Player.Joined += PlayerHandler.Joined; } public void UnregisterEvents() { @@ -89,6 +94,7 @@ public void UnregisterEvents() Player.ChangingItem -= PlayerHandler.ChangingItem; Player.UsedItem -= PlayerHandler.UsedItem; + Player.Joined -= PlayerHandler.Joined; } } } \ No newline at end of file diff --git a/SCP294.csproj b/SCP294.csproj index 46b5554..920538c 100644 --- a/SCP294.csproj +++ b/SCP294.csproj @@ -10,6 +10,8 @@ SCP294 Ultimate294 v4.8 + 9 + latest 512 true @@ -34,6 +36,7 @@ + diff --git a/Types/Config/DrinkCallbacks.cs b/Types/Config/DrinkCallbacks.cs index b7cee18..109b995 100644 --- a/Types/Config/DrinkCallbacks.cs +++ b/Types/Config/DrinkCallbacks.cs @@ -155,6 +155,29 @@ public static void LimitSizeOnDrinks(Player player) { player.Scale = new Vector3(player.Scale.x, player.Scale.y, SCP294.Instance.Config.MinSizeFromDrink.z); } + + // VOICE + SCP294.Instance.PlayerVoicePitch[player.UserId] = Mathf.Clamp((1-player.Scale.y) + 1f,0.1f,2f); + } + + public static void Helium(Player player) + { + SCP294.Instance.PlayerVoicePitch[player.UserId] = 1.6f; + Timing.CallDelayed(90f, () => + { + // VOICE + SCP294.Instance.PlayerVoicePitch[player.UserId] = Mathf.Clamp((1 - player.Scale.y) + 1f, 0.1f, 2f); + }); + } + + public static void Sulfur(Player player) + { + SCP294.Instance.PlayerVoicePitch[player.UserId] = 0.4f; + Timing.CallDelayed(90f, () => + { + // VOICE + SCP294.Instance.PlayerVoicePitch[player.UserId] = Mathf.Clamp((1 - player.Scale.y) + 1f, 0.1f, 2f); + }); } } } diff --git a/Types/Config/DrinkList.cs b/Types/Config/DrinkList.cs index 6ccf2cc..3bca302 100644 --- a/Types/Config/DrinkList.cs +++ b/Types/Config/DrinkList.cs @@ -1817,7 +1817,6 @@ public class DrinkList "Superfluid", "Superfluid Helium", "Liquid Helium", - "Helium", "Helium-4" }, AntiColaModel = false, @@ -2931,6 +2930,35 @@ public class DrinkList } }, // Rest in peace, Soup + new CustomDrink() + { + DrinkNames = new List(){ + "Helium", + "High Pitch" + }, + AntiColaModel = true, + BackfireChance = 0f, + DrinkMessage = "Cool party trick... (Voice is now high pitched for 1.5 minutes)", + DrinkEffects = new List(){ + + }, + DrinkCallback = DrinkCallbacks.Helium + }, // Helium VOICE CHANGE + new CustomDrink() + { + DrinkNames = new List(){ + "Sulfur", + "Sulfur Hexaflouride", + "Low Pitch" + }, + AntiColaModel = false, + BackfireChance = 0f, + DrinkMessage = "Cool party trick... (Voice is now low pitched for 1.5 minutes)", + DrinkEffects = new List(){ + + }, + DrinkCallback = DrinkCallbacks.Sulfur + }, // Sulfur VOICE CHANGE }; } }