using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using Photon.Voice;
using Photon.Voice.PUN;
using Photon.Voice.Unity;
using UnityEngine;
using VoiceCurse.Events;
using VoiceCurse.Handlers;
using VoiceCurse.Interfaces;
using VoiceCurse.Voice;
using Vosk;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.Ryocery.VoiceCurse")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+e7a6e383b4c68c3863acc28e050f189bcba743e0")]
[assembly: AssemblyProduct("com.github.Ryocery.VoiceCurse")]
[assembly: AssemblyTitle("VoiceCurse")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace VoiceCurse
{
public class Config
{
public ConfigEntry<bool> EnableDebugLogs { get; private set; }
public ConfigEntry<float> GlobalCooldown { get; private set; }
public ConfigEntry<bool> LaunchEnabled { get; private set; }
public ConfigEntry<string> LaunchKeywords { get; private set; }
public ConfigEntry<float> LaunchStunDuration { get; private set; }
public ConfigEntry<float> LaunchForceLowerBound { get; private set; }
public ConfigEntry<float> LaunchForceHigherBound { get; private set; }
public ConfigEntry<bool> AfflictionEnabled { get; private set; }
public ConfigEntry<float> AfflictionMinPercent { get; private set; }
public ConfigEntry<float> AfflictionMaxPercent { get; private set; }
public ConfigEntry<bool> AfflictionTemperatureSwapEnabled { get; private set; }
public ConfigEntry<string> AfflictionKeywordsInjury { get; private set; }
public ConfigEntry<string> AfflictionKeywordsHunger { get; private set; }
public ConfigEntry<string> AfflictionKeywordsCold { get; private set; }
public ConfigEntry<string> AfflictionKeywordsHot { get; private set; }
public ConfigEntry<string> AfflictionKeywordsPoison { get; private set; }
public ConfigEntry<string> AfflictionKeywordsSpores { get; private set; }
public ConfigEntry<bool> TransmuteEnabled { get; private set; }
public ConfigEntry<bool> TransmuteDeathEnabled { get; private set; }
public ConfigEntry<bool> TransmuteMilkEnabled { get; private set; }
public ConfigEntry<bool> TransmuteCactusEnabled { get; private set; }
public ConfigEntry<bool> TransmuteCoconutEnabled { get; private set; }
public ConfigEntry<bool> TransmuteAppleEnabled { get; private set; }
public ConfigEntry<bool> TransmuteBananaEnabled { get; private set; }
public ConfigEntry<bool> TransmuteEggEnabled { get; private set; }
public ConfigEntry<bool> TransmuteFruitEnabled { get; private set; }
public ConfigEntry<bool> TransmuteMushroomEnabled { get; private set; }
public ConfigEntry<bool> TransmuteBallEnabled { get; private set; }
public ConfigEntry<bool> DeathEnabled { get; private set; }
public ConfigEntry<string> DeathKeywords { get; private set; }
public ConfigEntry<bool> ZombifyEnabled { get; private set; }
public ConfigEntry<string> ZombifyKeywords { get; private set; }
public ConfigEntry<bool> SleepEnabled { get; private set; }
public ConfigEntry<string> SleepKeywords { get; private set; }
public ConfigEntry<bool> DropEnabled { get; private set; }
public ConfigEntry<string> DropKeywords { get; private set; }
public ConfigEntry<bool> ExplodeEnabled { get; private set; }
public ConfigEntry<string> ExplodeKeywords { get; private set; }
public ConfigEntry<float> ExplodeRadius { get; private set; }
public ConfigEntry<float> ExplodeDamage { get; private set; }
public ConfigEntry<float> ExplodeStunDuration { get; private set; }
public ConfigEntry<float> ExplodeForceLowerBound { get; private set; }
public ConfigEntry<float> ExplodeForceHigherBound { get; private set; }
public ConfigEntry<bool> SlipEnabled { get; private set; }
public ConfigEntry<string> SlipKeywords { get; private set; }
public ConfigEntry<float> SlipStunDuration { get; private set; }
public ConfigEntry<bool> BananaBombEnabled { get; private set; }
public ConfigEntry<float> BananaBombChance { get; private set; }
public ConfigEntry<int> BananaBombAmount { get; private set; }
public ConfigEntry<bool> SacrificeEnabled { get; private set; }
public ConfigEntry<string> SacrificeKeywords { get; private set; }
public ConfigEntry<float> SacrificeCooldown { get; private set; }
public ConfigEntry<bool> BlindEnabled { get; private set; }
public ConfigEntry<string> BlindKeywords { get; private set; }
public ConfigEntry<float> BlindDuration { get; private set; }
public Config(ConfigFile config)
{
EnableDebugLogs = config.Bind<bool>("Global", "EnableDebugLogs", true, "Enable debug logs for speech detection.");
GlobalCooldown = config.Bind<float>("Global", "GlobalCooldown", 2f, "The time in seconds that must pass before another voice event can be triggered.");
LaunchEnabled = config.Bind<bool>("Event.Launch", "Enabled", true, "Enable the Launch event.");
LaunchKeywords = config.Bind<string>("Event.Launch", "Keywords", "launch, fly, boost, ascend, lift, up, cannon, canon, rocket, soar, jump, spring, catapult, fling, hurl, propel, shoot, skyrocket, takeoff, left, right, forward, back, yeet, lob, pitch, chuck, heave, airborne, levitate, hover, elevate, rise, vault, leap, hop, eject, thrust, projectile, missile, space, orbit", "List of keywords that trigger the launch event, separated by commas.");
LaunchForceLowerBound = config.Bind<float>("Event.Launch", "ForceLowerBound", 1500f, "Lowest possible amount of force applied to the player upon launching..");
LaunchForceHigherBound = config.Bind<float>("Event.Launch", "ForceHigherBound", 3000f, "Highest possible amount of force applied to the player upon launching..");
LaunchStunDuration = config.Bind<float>("Event.Launch", "StunDuration", 3f, "Duration in seconds the player will be stunned/ragdolled after launching.");
AfflictionEnabled = config.Bind<bool>("Event.Affliction", "Enabled", true, "Enable the Affliction event.");
AfflictionMinPercent = config.Bind<float>("Event.Affliction", "MinPercent", 0.2f, "The minimum percentage (0.0 to 1.0) of the status bar to fill when triggered.");
AfflictionMaxPercent = config.Bind<float>("Event.Affliction", "MaxPercent", 0.6f, "The maximum percentage (0.0 to 1.0) of the status bar to fill when triggered.");
AfflictionTemperatureSwapEnabled = config.Bind<bool>("Event.Affliction", "TemperatureSwapEnabled", true, "If enabled, attempting to get rid of hot or cold by saying the opposite will just swap the existing effect to the new one and add on top of that to prevent cheesing it.");
AfflictionKeywordsInjury = config.Bind<string>("Event.Affliction", "KeywordsInjury", "damage, hurt, injury, injured, pain, harm, wound, hit, bleed, bruise, cut, slash, orange, ache, sore, trauma, gash, scrape, laceration, tear, torn, broken, fracture, sprain, puncture, stab, maim, cripple, batter", "Keywords that trigger physical injury.");
AfflictionKeywordsHunger = config.Bind<string>("Event.Affliction", "KeywordsHunger", "hunger, hungry, starving, starve, food, malnourishment, famished, eat, snack, meal, yellow, appetite, crave, craving, ravenous, peckish, feast, feed, sustenance, nourishment, nutrition, consume", "Keywords that trigger hunger.");
AfflictionKeywordsCold = config.Bind<string>("Event.Affliction", "KeywordsCold", "freezing, cool, cold, blizzard, shiver, ice, frozen, chill, frigid, winter, blue, frost, arctic, polar, glacier, icicle, hypothermia, numb, shivering, freeze", "Keywords that trigger cold status.");
AfflictionKeywordsHot = config.Bind<string>("Event.Affliction", "KeywordsHot", "hot, fire, melt, scorching, heat, burn, pyro, flame, summer, cook, hell, red, sizzle, sear, swelter, boil, roast, bake, baking, scald, inferno, blaze, blazing, ignite, combust, incinerate", "Keywords that trigger hot status.");
AfflictionKeywordsPoison = config.Bind<string>("Event.Affliction", "KeywordsPoison", "poison, sick, vomit, toxic, venom, contaminate, purple, nausea, nauseous, intoxicate, pollute, taint, corrupt, disease, ill, ailment, malady", "Keywords that trigger poison status.");
AfflictionKeywordsSpores = config.Bind<string>("Event.Affliction", "KeywordsSpores", "spore, pink", "Keywords that trigger spore status.");
TransmuteEnabled = config.Bind<bool>("Event.Transmute", "Enabled", true, "Enable the Transmute event.");
TransmuteDeathEnabled = config.Bind<bool>("Event.Transmute", "EnableDeath", true, "Enable instant death when transmute is triggered (by using your flesh as something to transmute). If disabled, you will just take damage instead (your flesh partially transmutes).");
TransmuteMilkEnabled = config.Bind<bool>("Event.Transmute", "EnableMilk", true, "Enable 'milk/calcium' -> Fortified Milk");
TransmuteCactusEnabled = config.Bind<bool>("Event.Transmute", "EnableCactus", true, "Enable 'cactus' -> Cactus");
TransmuteCoconutEnabled = config.Bind<bool>("Event.Transmute", "EnableCoconut", true, "Enable 'coconut' -> Coconut");
TransmuteAppleEnabled = config.Bind<bool>("Event.Transmute", "EnableApple", true, "Enable 'apple/berry' -> Crispberries");
TransmuteBananaEnabled = config.Bind<bool>("Event.Transmute", "EnableBanana", true, "Enable 'banana' -> Peel");
TransmuteEggEnabled = config.Bind<bool>("Event.Transmute", "EnableEgg", true, "Enable 'egg' -> Egg");
TransmuteFruitEnabled = config.Bind<bool>("Event.Transmute", "EnableFruit", true, "Enable 'fruit' -> Random Fruit");
TransmuteMushroomEnabled = config.Bind<bool>("Event.Transmute", "EnableMushroom", true, "Enable 'mushroom/fungus' -> Mushroom");
TransmuteBallEnabled = config.Bind<bool>("Event.Transmute", "EnableBall", true, "Enable 'ball' -> Basketball");
DeathEnabled = config.Bind<bool>("Event.Death", "Enabled", true, "Enable the Death event.");
DeathKeywords = config.Bind<string>("Event.Death", "Keywords", "die, death, dead, suicide, kill, deceased, skeleton, skull, bone, perish, demise, expire, fatal, mortal, slain, dying, corpse, cadaver, lifeless, cease, extinct, eliminate, terminate, execute, obliterate, annihilate, eradicate, end, finish, doom, grave, burial, coffin, casket, tomb, crypt, reaper, grim", "List of keywords that trigger the death event, separated by commas.");
ZombifyEnabled = config.Bind<bool>("Event.Zombify", "Enabled", true, "Enable the Zombify event.");
ZombifyKeywords = config.Bind<string>("Event.Zombify", "Keywords", "zombie, zombify, zombified, walker, ghoul, bitten, bite, brain, rot, decay, infected, infection, plague, pandemic, virus, outbreak, cannibal, flesh, meat, undead, risen, horde, apocalypse, reanimate, lurker, creeper, crawler, groaning, groan, moan, growl, snarl", "List of keywords that trigger the zombify event, separated by commas.");
SleepEnabled = config.Bind<bool>("Event.Sleep", "Enabled", true, "Enable the Sleep event.");
SleepKeywords = config.Bind<string>("Event.Sleep", "Keywords", "faint, sleep, exhausted, sleepy, tired, bed, nap, rest, slumber, doze, snooze, pass out, knockout, blackout, coma, narc, drowsy, unconscious, collapse, zonk, conk, yawn, fatigue, fatigued, weary, lethargic, sluggish, drained, wiped, beat, worn, spent, shut-eye, shuteye, siesta, catnap, dreamland, nodding off, drift off, lights out, out cold", "List of keywords that trigger the sleep event, separated by commas.");
DropEnabled = config.Bind<bool>("Event.Drop", "Enabled", true, "Enable the Drop event.");
DropKeywords = config.Bind<string>("Event.Drop", "Keywords", "drop, oops, whoops, butterfingers, fumble, release, discard, off, loss, lose, let go, slip away, misplace, clumsy, accident, unhand, relinquish, surrender, abandon, ditch, shed, cast, toss, throw away, get rid", "List of keywords that trigger the drop event, separated by commas.");
ExplodeEnabled = config.Bind<bool>("Event.Explode", "Enabled", true, "Enable the Explode event.");
ExplodeKeywords = config.Bind<string>("Event.Explode", "Keywords", "explosion, dynamite, grenade, explode, blow, blast, boom, nuke, bomb, nuclear, detonate, detonation, explosive, kaboom, burst", "List of keywords that trigger the explode event, separated by commas.");
ExplodeRadius = config.Bind<float>("Event.Explode", "Radius", 6f, "The radius of the explosion effect and damage.");
ExplodeDamage = config.Bind<float>("Event.Explode", "DamagePercent", 0.4f, "The percentage of injury (0.0 to 1.0) applied to the player.");
ExplodeStunDuration = config.Bind<float>("Event.Explode", "StunDuration", 3f, "Duration in seconds the player will be stunned/ragdolled after explosion.");
ExplodeForceLowerBound = config.Bind<float>("Event.Explode", "ForceLowerBound", 2000f, "Lowest possible amount of explosion force applied.");
ExplodeForceHigherBound = config.Bind<float>("Event.Explode", "ForceHigherBound", 3000f, "Highest possible amount of explosion force applied.");
SlipEnabled = config.Bind<bool>("Event.Slip", "Enabled", true, "Enable the Slip event.");
SlipKeywords = config.Bind<string>("Event.Slip", "Keywords", "fuck, asshole, bastard, bitch, fag, damn, crap, slip, slide, trip, fall, fell, stumble, tumble, topple, stagger, wobble, skid, slick, peel, unbalanced, unstable, tilt", "List of keywords that trigger the slip event, separated by commas.");
SlipStunDuration = config.Bind<float>("Event.Slip", "StunDuration", 2f, "Duration in seconds the player will be stunned/ragdolled after slipping.");
BananaBombEnabled = config.Bind<bool>("Event.Slip", "BananaBombEnabled", true, "Enable the rare Banana Bomb easter egg on slip.");
BananaBombChance = config.Bind<float>("Event.Slip", "BananaBombChance", 0.05f, "Chance (0.0 - 1.0) for a slip to trigger a Banana Bomb.");
BananaBombAmount = config.Bind<int>("Event.Slip", "BananaBombAmount", 30, "Amount of bananas to spawn during a Banana Bomb.");
SacrificeEnabled = config.Bind<bool>("Event.Sacrifice", "Enabled", true, "Enable the Sacrifice event.");
SacrificeKeywords = config.Bind<string>("Event.Sacrifice", "Keywords", "sacrifice, trade, revive, resurrect, exchange, soul, offer", "List of keywords that trigger the sacrifice event.");
SacrificeCooldown = config.Bind<float>("Event.Sacrifice", "Cooldown", 300f, "Cooldown in seconds for the sacrifice event.");
BlindEnabled = config.Bind<bool>("Event.Blind", "Enabled", true, "Enable the Blind event.");
BlindKeywords = config.Bind<string>("Event.Blind", "Keywords", "blind, flash, eyes, vision, can't see, my eyes", "List of keywords that trigger the blind event.");
BlindDuration = config.Bind<float>("Event.Blind", "Duration", 15f, "Duration in seconds the player will be blinded.");
}
}
[BepInPlugin("com.github.Ryocery.VoiceCurse", "VoiceCurse", "1.1.0")]
public class Plugin : BaseUnityPlugin
{
private Config? _config;
private VoiceHandler? _voiceHandler;
private Harmony? _harmony;
public const string Id = "com.github.Ryocery.VoiceCurse";
private static ManualLogSource Log { get; set; }
public static string Name => "VoiceCurse";
public static string Version => "1.1.0";
private void Awake()
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)("Plugin " + Name + " is loading..."));
_config = new Config(((BaseUnityPlugin)this).Config);
_harmony = new Harmony("com.github.Ryocery.VoiceCurse");
_harmony.PatchAll();
Log.LogInfo((object)"Harmony Patches applied.");
string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
if (string.IsNullOrEmpty(text))
{
text = Paths.PluginPath;
}
_voiceHandler = new VoiceHandler(Log, _config, text);
Log.LogInfo((object)("Plugin " + Name + " loaded successfully."));
}
private void Update()
{
_voiceHandler?.Update();
}
private void OnDestroy()
{
_voiceHandler?.Dispose();
Harmony? harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
}
}
namespace VoiceCurse.Voice
{
public class VoiceHook : MonoBehaviour
{
private VoiceHandler? _manager;
private Recorder? _recorder;
private bool _hasInjected;
public void Initialize(VoiceHandler manager, Recorder recorder)
{
_manager = manager;
_recorder = recorder;
CheckIfReady();
}
private void Update()
{
if (!_hasInjected)
{
CheckIfReady();
}
}
private void CheckIfReady()
{
if (!Object.op_Implicit((Object)(object)_recorder) || _manager == null || !_recorder.IsCurrentlyTransmitting)
{
return;
}
FieldInfo field = typeof(Recorder).GetField("voice", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (!(field == null))
{
object? value = field.GetValue(_recorder);
LocalVoice val = (LocalVoice)((value is LocalVoice) ? value : null);
if (val != null)
{
_manager.OnPhotonVoiceReady(_recorder, val);
_hasInjected = true;
}
}
}
private void PhotonVoiceCreated(PhotonVoiceCreatedParams p)
{
if (!_hasInjected && !((Object)(object)_recorder == (Object)null))
{
_manager?.OnPhotonVoiceReady(_recorder, p.Voice);
_hasInjected = true;
}
}
}
public class VoiceProcessor : IProcessor<float>, IDisposable
{
[CompilerGenerated]
private IVoiceRecognizer <recognizer>P;
[CompilerGenerated]
private int <inputSampleRate>P;
private const int TargetSampleRate = 16000;
public VoiceProcessor(IVoiceRecognizer recognizer, int inputSampleRate)
{
<recognizer>P = recognizer;
<inputSampleRate>P = inputSampleRate;
base..ctor();
}
public float[]? Process(float[]? buf)
{
if (buf == null || buf.Length == 0)
{
return buf;
}
if (Math.Abs(<inputSampleRate>P - 16000) < 100)
{
ProcessDirect(buf);
return buf;
}
float num = (float)<inputSampleRate>P / 16000f;
int num2 = (int)((float)buf.Length / num);
if (num2 == 0)
{
return buf;
}
short[] array = new short[num2];
for (int i = 0; i < num2; i++)
{
float num3 = (float)i * num;
int num4 = (int)num3;
float num5 = num3 - (float)num4;
if (num4 >= buf.Length - 1)
{
array[i] = FloatToShort(buf[^1]);
continue;
}
float num6 = buf[num4];
float num7 = buf[num4 + 1];
float sample = num6 + (num7 - num6) * num5;
array[i] = FloatToShort(sample);
}
<recognizer>P.FeedAudio(array, array.Length);
return buf;
}
private void ProcessDirect(float[] buf)
{
short[] array = new short[buf.Length];
for (int i = 0; i < buf.Length; i++)
{
array[i] = FloatToShort(buf[i]);
}
<recognizer>P.FeedAudio(array, array.Length);
}
private static short FloatToShort(float sample)
{
float num = ((sample > 1f) ? 1f : ((!(sample < -1f)) ? sample : (-1f)));
sample = num;
return (short)(sample * 32767f);
}
public void Dispose()
{
}
}
public class VoiceRecognizer : IVoiceRecognizer, IDisposable
{
private readonly VoskRecognizer _recognizer;
private readonly ConcurrentQueue<short[]> _audioQueue = new ConcurrentQueue<short[]>();
private Thread? _workerThread;
private volatile bool _isRunning;
private const int MaxQueueSize = 50;
public event Action<string>? OnPhraseRecognized;
public event Action<string>? OnPartialResult;
public VoiceRecognizer(Model model, float sampleRate)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
try
{
_recognizer = new VoskRecognizer(model, sampleRate);
_recognizer.SetMaxAlternatives(0);
_recognizer.SetWords(true);
Debug.Log((object)$"[VoiceCurse] Initialized VoskRecognizer with Rate: {sampleRate} Hz");
}
catch (Exception ex)
{
Debug.LogError((object)("Failed to create VoskRecognizer: " + ex.Message));
throw;
}
}
public void Start()
{
if (!_isRunning)
{
_isRunning = true;
_workerThread = new Thread(ProcessAudioLoop)
{
IsBackground = true,
Name = "VoiceCurse Worker"
};
_workerThread.Start();
}
}
public void Stop()
{
_isRunning = false;
Thread workerThread = _workerThread;
if (workerThread != null && workerThread.IsAlive)
{
_workerThread.Join(500);
}
}
public void FeedAudio(short[] pcmData, int length)
{
if (!_isRunning)
{
return;
}
if (_audioQueue.Count >= 50)
{
short[] result;
while (_audioQueue.TryDequeue(out result))
{
}
}
_audioQueue.Enqueue(pcmData);
}
private void ProcessAudioLoop()
{
while (_isRunning)
{
if (_audioQueue.TryDequeue(out short[] result))
{
try
{
if (_recognizer.AcceptWaveform(result, result.Length))
{
string json = _recognizer.Result();
ExtractAndFire(json, isPartial: false);
}
else
{
string json2 = _recognizer.PartialResult();
ExtractAndFire(json2, isPartial: true);
}
}
catch (Exception ex)
{
Debug.LogError((object)("[VoiceCurse] Vosk Error: " + ex.Message));
}
}
else
{
Thread.Sleep(10);
}
}
}
private void ExtractAndFire(string json, bool isPartial)
{
if (string.IsNullOrEmpty(json))
{
return;
}
string text = (isPartial ? "\"partial\"" : "\"text\"");
int num = json.IndexOf(text + " :", StringComparison.Ordinal);
if (num == -1)
{
num = json.IndexOf(text + ":", StringComparison.Ordinal);
}
if (num == -1)
{
return;
}
int num2 = json.IndexOf("\"", num + text.Length + 1, StringComparison.Ordinal) + 1;
int num3 = json.LastIndexOf("\"", StringComparison.Ordinal);
if (num3 <= num2)
{
return;
}
string text2 = json.Substring(num2, num3 - num2);
if (!string.IsNullOrWhiteSpace(text2))
{
if (isPartial)
{
this.OnPartialResult?.Invoke(text2);
}
else
{
this.OnPhraseRecognized?.Invoke(text2);
}
}
}
public void Dispose()
{
Stop();
_recognizer.Dispose();
}
}
}
namespace VoiceCurse.Interfaces
{
public interface IVoiceEvent
{
bool TryExecute(string spokenWord, string fullSentence);
void PlayEffects(Vector3 position);
void PlayEffects(Character origin, Vector3 position, string detail);
}
public interface IVoiceRecognizer : IDisposable
{
event Action<string> OnPhraseRecognized;
event Action<string> OnPartialResult;
void FeedAudio(short[] pcmData, int length);
void Start();
void Stop();
}
}
namespace VoiceCurse.Handlers
{
public class EventHandler
{
public static readonly Dictionary<string, IVoiceEvent> Events = new Dictionary<string, IVoiceEvent>();
private readonly Dictionary<string, int> _previousWordCounts = new Dictionary<string, int>();
public EventHandler(Config config)
{
Events.Clear();
RegisterEvents(config);
}
private static void RegisterEvents(Config config)
{
IEnumerable<Type> enumerable = from t in Assembly.GetExecutingAssembly().GetTypes()
where typeof(VoiceEventBase).IsAssignableFrom(t) && !t.IsAbstract && t.IsClass
select t;
foreach (Type item in enumerable)
{
try
{
if (Activator.CreateInstance(item, config) is IVoiceEvent value)
{
string text = item.Name.Replace("Event", "");
Events[text] = value;
if (config.EnableDebugLogs.Value)
{
Debug.Log((object)("[VoiceCurse] Registered event: " + text));
}
}
}
catch (Exception ex)
{
Debug.LogError((object)("[VoiceCurse] Failed to register event " + item.Name + ": " + ex.Message));
}
}
}
public void HandleSpeech(string text, bool isFinal)
{
if (string.IsNullOrWhiteSpace(text))
{
return;
}
string text2 = text.ToLowerInvariant();
string[] array = text2.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, int> dictionary = new Dictionary<string, int>();
string[] array2 = array;
string key;
int value;
foreach (string text3 in array2)
{
dictionary.TryAdd(text3, 0);
key = text3;
value = dictionary[key]++;
}
foreach (KeyValuePair<string, int> item in dictionary)
{
item.Deconstruct(out key, out value);
string text4 = key;
int num = value;
int num2 = 0;
if (_previousWordCounts.TryGetValue(text4, out var value2))
{
num2 = value2;
}
int num3 = num - num2;
if (num3 <= 0)
{
continue;
}
for (int j = 0; j < num3; j++)
{
foreach (IVoiceEvent value3 in Events.Values)
{
value3.TryExecute(text4, text2);
}
}
}
_previousWordCounts.Clear();
foreach (KeyValuePair<string, int> item2 in dictionary)
{
_previousWordCounts[item2.Key] = item2.Value;
}
if (isFinal)
{
_previousWordCounts.Clear();
}
}
}
public class NetworkHandler : IOnEventCallback
{
private const byte VoiceCurseEventCode = 187;
private static MonoBehaviour? _connectionLog;
private static MethodInfo? _addMessageMethod;
public static void SendCurseEvent(string spokenWord, string matchedKeyword, string eventName, string? detail, string? payload, Vector3 position)
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Expected O, but got Unknown
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
object[] array = new object[7]
{
PhotonNetwork.LocalPlayer.ActorNumber,
spokenWord,
matchedKeyword,
eventName,
detail ?? string.Empty,
position,
payload ?? string.Empty
};
RaiseEventOptions val = new RaiseEventOptions
{
Receivers = (ReceiverGroup)1
};
SendOptions val2 = default(SendOptions);
((SendOptions)(ref val2)).Reliability = true;
SendOptions val3 = val2;
PhotonNetwork.RaiseEvent((byte)187, (object)array, val, val3);
}
public void OnEvent(EventData photonEvent)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
if (photonEvent.Code != 187)
{
return;
}
object[] array = (object[])photonEvent.CustomData;
int num = (int)array[0];
string fullWord = (string)array[1];
string keyword = (string)array[2];
string text = (string)array[3];
string text2 = (string)array[4];
Vector3 position = (Vector3)array[5];
string text3 = ((array.Length > 6) ? ((string)array[6]) : string.Empty);
string playerName = "Unknown";
Color color = Color.white;
Player player = PhotonNetwork.CurrentRoom.GetPlayer(num, false);
if (player != null)
{
playerName = player.NickName;
}
Character val = FindCharacterByActorNumber(num);
if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.refs?.customization))
{
color = val.refs.customization.PlayerColor;
}
DisplayNotification(playerName, color, fullWord, keyword, text, text2);
if (EventHandler.Events.TryGetValue(text, out IVoiceEvent value))
{
if (Object.op_Implicit((Object)(object)val))
{
string detail = ((!string.IsNullOrEmpty(text3)) ? text3 : text2);
value.PlayEffects(val, position, detail);
}
else
{
value.PlayEffects(position);
}
}
}
private static Character? FindCharacterByActorNumber(int actorNumber)
{
return ((IEnumerable<Character>)Character.AllCharacters).FirstOrDefault((Func<Character, bool>)((Character c) => ((MonoBehaviourPun)c).photonView.OwnerActorNr == actorNumber));
}
private static void DisplayNotification(string playerName, Color color, string fullWord, string keyword, string eventName, string detail)
{
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)_connectionLog))
{
Object obj = Object.FindFirstObjectByType(Type.GetType("PlayerConnectionLog, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"));
_connectionLog = (MonoBehaviour?)(((object)((obj is MonoBehaviour) ? obj : null)) ?? ((object)((IEnumerable<MonoBehaviour>)Object.FindObjectsByType<MonoBehaviour>((FindObjectsSortMode)0)).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour m) => ((object)m).GetType().Name == "PlayerConnectionLog"))));
_addMessageMethod = null;
}
if (!Object.op_Implicit((Object)(object)_connectionLog))
{
return;
}
if (_addMessageMethod == null)
{
_addMessageMethod = ((object)_connectionLog).GetType().GetMethod("AddMessage", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (_addMessageMethod == null)
{
return;
}
string text = "#" + ColorUtility.ToHtmlStringRGB(color);
string text2 = fullWord;
int num = fullWord.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);
if (num >= 0)
{
string text3 = fullWord.Substring(0, num);
string text4 = fullWord.Substring(num, keyword.Length);
int num2 = num + keyword.Length;
string text5 = fullWord.Substring(num2, fullWord.Length - num2);
text2 = text3 + "<color=#8B0000>" + text4 + "</color>" + text5;
}
string text6 = "<color=" + text + ">" + playerName + " said \"" + text2 + "\" which triggered </color><color=#FFA500>" + eventName + "</color><color=#FAFA33>" + (string.IsNullOrEmpty(detail) ? "" : (" (" + detail + ")")) + "</color>";
try
{
_addMessageMethod.Invoke(_connectionLog, new object[1] { text6 });
}
catch
{
_connectionLog = null;
}
}
}
public class VoiceHandler : IDisposable
{
private readonly ManualLogSource _log;
private readonly Config _config;
private IVoiceRecognizer? _recognizer;
private EventHandler? _eventHandler;
private NetworkHandler? _networker;
private Model? _voskModel;
private VoiceHook? _activeHook;
private readonly ConcurrentQueue<Action> _mainThreadActions = new ConcurrentQueue<Action>();
private volatile string _lastPartialText = "";
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
public VoiceHandler(ManualLogSource logger, Config config, string pluginDir)
{
_log = logger;
_config = config;
Initialize(pluginDir);
}
private void Initialize(string pluginDir)
{
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
_log.LogInfo((object)"Initializing VoiceCurse Manager...");
if (Directory.Exists(pluginDir))
{
SetDllDirectory(pluginDir);
}
_eventHandler = new EventHandler(_config);
_networker = new NetworkHandler();
PhotonNetwork.AddCallbackTarget((object)_networker);
string text = Path.Combine(pluginDir, "model-en-us");
if (Directory.Exists(text))
{
try
{
_voskModel = new Model(text);
_log.LogInfo((object)"Vosk Model loaded successfully.");
return;
}
catch (Exception ex)
{
_log.LogError((object)("Failed to load Vosk Model: " + ex.Message));
return;
}
}
_log.LogError((object)("Vosk model not found at: " + text));
}
public void Update()
{
Action result;
while (_mainThreadActions.TryDequeue(out result))
{
result();
}
if (!Object.op_Implicit((Object)(object)_activeHook) && Object.op_Implicit((Object)(object)Character.localCharacter))
{
TryHookIntoPhotonVoice();
}
}
private void TryHookIntoPhotonVoice()
{
PhotonVoiceView component = ((Component)Character.localCharacter).GetComponent<PhotonVoiceView>();
if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.RecorderInUse))
{
Recorder recorderInUse = component.RecorderInUse;
VoiceHook voiceHook = ((Component)recorderInUse).gameObject.AddComponent<VoiceHook>();
voiceHook.Initialize(this, recorderInUse);
_activeHook = voiceHook;
_log.LogInfo((object)("[VoiceCurse] Hooked into Recorder on: " + ((Object)((Component)recorderInUse).gameObject).name));
}
}
public void OnPhotonVoiceReady(Recorder recorder, LocalVoice voice)
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
int num = 48000;
VoiceInfo info = voice.Info;
if (((VoiceInfo)(ref info)).SamplingRate > 0)
{
info = voice.Info;
num = ((VoiceInfo)(ref info)).SamplingRate;
}
_log.LogInfo((object)$"[VoiceCurse] Photon Input Rate: {num} Hz. Initializing Vosk at 16000 Hz.");
SetupVoiceRecognition();
if (_recognizer is VoiceRecognizer && voice is LocalVoiceAudio<float> val)
{
((LocalVoiceFramed<float>)(object)val).AddPostProcessor(new IProcessor<float>[1]
{
new VoiceProcessor(_recognizer, num)
});
_log.LogInfo((object)$"[VoiceCurse] Audio Processor attached. Resampling {num} -> 16000 Hz.");
}
else
{
_log.LogWarning((object)("[VoiceCurse] Could not attach processor. Voice type: " + ((object)voice).GetType().Name));
}
}
private void SetupVoiceRecognition()
{
if (_recognizer != null || _voskModel == null)
{
return;
}
try
{
_recognizer = new VoiceRecognizer(_voskModel, 16000f);
_recognizer.OnPhraseRecognized += delegate(string text)
{
string text2 = text;
_lastPartialText = "";
_mainThreadActions.Enqueue(delegate
{
_log.LogInfo((object)("[Recognized]: " + text2));
_eventHandler?.HandleSpeech(text2, isFinal: true);
});
};
_recognizer.OnPartialResult += delegate(string text)
{
if (!string.IsNullOrWhiteSpace(text) && text.Length >= 2 && !(_lastPartialText == text))
{
_lastPartialText = text;
string captured = text;
_mainThreadActions.Enqueue(delegate
{
_log.LogInfo((object)("[Partial]: " + captured));
_eventHandler?.HandleSpeech(captured, isFinal: false);
});
}
};
_recognizer.Start();
_log.LogInfo((object)"[VoiceCurse] Vosk Recognizer started.");
}
catch (Exception ex)
{
_log.LogError((object)("Failed to start Vosk Recognizer: " + ex.Message));
}
}
public void Dispose()
{
if (_networker != null)
{
PhotonNetwork.RemoveCallbackTarget((object)_networker);
}
_recognizer?.Stop();
_recognizer?.Dispose();
Model? voskModel = _voskModel;
if (voskModel != null)
{
voskModel.Dispose();
}
_log.LogInfo((object)"VoiceCurse Manager disposed.");
}
}
}
namespace VoiceCurse.Events
{
public class AfflictionEvent : VoiceEventBase
{
private readonly Dictionary<string, STATUSTYPE> _wordToType;
public AfflictionEvent(Config config)
: base(config)
{
_wordToType = new Dictionary<string, STATUSTYPE>();
LoadKeywordsForType(config.AfflictionKeywordsInjury.Value, (STATUSTYPE)0);
LoadKeywordsForType(config.AfflictionKeywordsHunger.Value, (STATUSTYPE)1);
LoadKeywordsForType(config.AfflictionKeywordsCold.Value, (STATUSTYPE)2);
LoadKeywordsForType(config.AfflictionKeywordsHot.Value, (STATUSTYPE)8);
LoadKeywordsForType(config.AfflictionKeywordsPoison.Value, (STATUSTYPE)3);
LoadKeywordsForType(config.AfflictionKeywordsSpores.Value, (STATUSTYPE)10);
}
private void LoadKeywordsForType(string configLine, STATUSTYPE type)
{
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
IEnumerable<string> enumerable = from w in configLine.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
select w.Trim().ToLowerInvariant() into w
where !string.IsNullOrWhiteSpace(w)
select w;
foreach (string item in enumerable)
{
_wordToType[item] = type;
}
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.AfflictionEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _wordToType.Keys;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Invalid comparison between Unknown and I4
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Invalid comparison between Unknown and I4
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
if (!Config.AfflictionEnabled.Value)
{
return false;
}
if (player.refs?.afflictions == null)
{
return false;
}
if (player.data.dead || player.data.fullyPassedOut)
{
return false;
}
if (!_wordToType.TryGetValue(matchedKeyword, out var value))
{
return false;
}
base.ExecutionDetail = ((object)(STATUSTYPE)(ref value)).ToString();
bool value2 = Config.AfflictionTemperatureSwapEnabled.Value;
bool flag = value2;
if (flag)
{
bool flag2 = (((int)value == 2 || (int)value == 8) ? true : false);
flag = flag2;
}
if (flag)
{
HandleTemperatureExchange(player, value);
}
float num = Random.Range(Config.AfflictionMinPercent.Value, Config.AfflictionMaxPercent.Value);
if (Config.EnableDebugLogs.Value)
{
Debug.Log((object)$"[VoiceCurse] Affliction Specifics: {value} ({num:P0})");
}
player.refs.afflictions.AddStatus(value, num, false, true);
return true;
}
private void HandleTemperatureExchange(Character player, STATUSTYPE incomingType)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
STATUSTYPE val = (STATUSTYPE)(((int)incomingType == 8) ? 2 : 8);
float currentStatus = player.refs.afflictions.GetCurrentStatus(val);
if (!(currentStatus <= 0.01f))
{
if (Config.EnableDebugLogs.Value)
{
Debug.Log((object)$"[VoiceCurse] Swapping {val} ({currentStatus:P0}) to {incomingType}");
}
player.refs.afflictions.SubtractStatus(val, currentStatus, false, false);
player.refs.afflictions.AddStatus(incomingType, currentStatus, false, true);
}
}
}
public class BlindEvent : VoiceEventBase
{
private readonly HashSet<string> _keywords = VoiceEventBase.ParseKeywords(config.BlindKeywords.Value);
private float _blindEndTime;
private Coroutine? _blindRoutine;
public BlindEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.BlindEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _keywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
if (!Config.BlindEnabled.Value)
{
return false;
}
if (player.data.dead)
{
return false;
}
float value = Config.BlindDuration.Value;
_blindEndTime = Time.time + value;
if (_blindRoutine == null)
{
ToggleBlindness(player, enable: true);
_blindRoutine = ((MonoBehaviour)player).StartCoroutine(BlindnessRoutine(player));
}
player.AddIllegalStatus("BLIND", value);
return true;
}
private IEnumerator BlindnessRoutine(Character player)
{
while (Time.time < _blindEndTime && Object.op_Implicit((Object)(object)player) && !player.data.dead)
{
yield return null;
}
if (Object.op_Implicit((Object)(object)player))
{
ToggleBlindness(player, enable: false);
}
_blindRoutine = null;
}
private static void ToggleBlindness(Character player, bool enable)
{
if (Object.op_Implicit((Object)(object)player) && player.refs != null && Object.op_Implicit((Object)(object)player.refs.customization) && Object.op_Implicit((Object)(object)player.refs.customization.refs))
{
Renderer blindRenderer = player.refs.customization.refs.blindRenderer;
if (Object.op_Implicit((Object)(object)blindRenderer))
{
((Component)blindRenderer).gameObject.SetActive(enable);
}
}
}
}
public class DeathEvent : VoiceEventBase
{
private readonly HashSet<string> _deathKeywords = VoiceEventBase.ParseKeywords(config.DeathKeywords.Value);
public DeathEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.DeathEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _deathKeywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
if (!Config.DeathEnabled.Value)
{
return false;
}
if (player.data.dead)
{
return false;
}
player.DieInstantly();
return true;
}
}
public class DropEvent : VoiceEventBase
{
private readonly HashSet<string> _keywords = VoiceEventBase.ParseKeywords(config.DropKeywords.Value);
public DropEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.DropEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _keywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
if (Config.DropEnabled.Value)
{
return !player.data.dead;
}
return false;
}
public override void PlayEffects(Character origin, Vector3 position)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
if (!PhotonNetwork.IsMasterClient || !Object.op_Implicit((Object)(object)origin) || origin.data.dead)
{
return;
}
ScatterBackpackContents(origin);
Transform transform = ((Component)origin.GetBodypart((BodypartType)0)).transform;
Vector3 val = transform.forward;
if (Vector3.Dot(val, Vector3.up) < 0f)
{
val = -val;
}
Vector3 val2 = origin.Center + val * 0.6f + Vector3.up * 0.5f;
for (byte b = 0; b < 4; b++)
{
if (!origin.player.GetItemSlot(b).IsEmpty())
{
origin.refs.items.photonView.RPC("DropItemFromSlotRPC", (RpcTarget)0, new object[2] { b, val2 });
val2 += Vector3.up * 0.3f;
}
}
origin.refs.items.photonView.RPC("EquipSlotRpc", (RpcTarget)0, new object[2] { -1, -1 });
}
private static void ScatterBackpackContents(Character player)
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
ItemSlot itemSlot = player.player.GetItemSlot((byte)3);
BackpackData val = default(BackpackData);
if (itemSlot.IsEmpty() || !itemSlot.data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
{
return;
}
Vector3 center = player.Center;
ItemSlot[] itemSlots = val.itemSlots;
PhotonView val5 = default(PhotonView);
foreach (ItemSlot val2 in itemSlots)
{
if (val2.IsEmpty())
{
continue;
}
string prefabName = val2.GetPrefabName();
if (!string.IsNullOrEmpty(prefabName))
{
Vector3 val3 = center + Random.insideUnitSphere * 0.5f;
val3.y = center.y + 0.5f;
GameObject val4 = PhotonNetwork.Instantiate("0_Items/" + prefabName, val3, Quaternion.identity, (byte)0, (object[])null);
if (val4.TryGetComponent<PhotonView>(ref val5))
{
val5.RPC("SetItemInstanceDataRPC", (RpcTarget)0, new object[1] { val2.data });
val5.RPC("SetKinematicRPC", (RpcTarget)0, new object[3]
{
false,
val3,
Quaternion.identity
});
}
val2.EmptyOut();
}
}
}
}
public class ExplodeEvent : VoiceEventBase
{
private readonly HashSet<string> _keywords = VoiceEventBase.ParseKeywords(config.ExplodeKeywords.Value);
private static GameObject? _cachedExplosionPrefab;
public ExplodeEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.ExplodeEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _keywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
if (!Config.ExplodeEnabled.Value)
{
return false;
}
return !player.data.dead;
}
public override void PlayEffects(Vector3 position)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)_cachedExplosionPrefab))
{
FindExplosionPrefab();
}
if (Object.op_Implicit((Object)(object)_cachedExplosionPrefab))
{
Object.Instantiate<GameObject>(_cachedExplosionPrefab, position, Quaternion.identity);
}
Character localCharacter = Character.localCharacter;
if (!Object.op_Implicit((Object)(object)localCharacter) || localCharacter.data.dead)
{
return;
}
float num = Vector3.Distance(localCharacter.Center, position);
if (num <= Config.ExplodeRadius.Value)
{
if (Object.op_Implicit((Object)(object)localCharacter.refs.afflictions))
{
localCharacter.refs.afflictions.AddStatus((STATUSTYPE)0, Config.ExplodeDamage.Value, false, true);
}
localCharacter.Fall(Config.ExplodeStunDuration.Value, 0f);
Vector3 val = localCharacter.Center - position;
Vector3 val2 = ((Vector3)(ref val)).normalized;
val2 += Vector3.up * 0.6f;
((Vector3)(ref val2)).Normalize();
float num2 = Random.Range(Config.ExplodeForceLowerBound.Value, Config.ExplodeForceHigherBound.Value);
localCharacter.AddForce(val2 * num2, 1f, 1f);
}
}
private static void FindExplosionPrefab()
{
Dynamite val = Resources.FindObjectsOfTypeAll<Dynamite>().FirstOrDefault();
if (Object.op_Implicit((Object)(object)val))
{
_cachedExplosionPrefab = val.explosionPrefab;
}
}
}
public class LaunchEvent : VoiceEventBase
{
private readonly HashSet<string> _keywords = VoiceEventBase.ParseKeywords(config.LaunchKeywords.Value);
private static GameObject? _cachedLaunchSFX;
public LaunchEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.LaunchEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _keywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
//IL_01be: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_016f: Unknown result type (might be due to invalid IL or missing references)
if (!Config.LaunchEnabled.Value)
{
return false;
}
if (player.data.dead || player.data.fullyPassedOut)
{
return false;
}
if (_cachedLaunchSFX == null)
{
FindLaunchSFX();
}
player.Fall(Config.LaunchStunDuration.Value, 0f);
Vector3 normalized = ((Vector3)(ref player.data.lookDirection_Flat)).normalized;
Vector3 val = Vector3.Cross(Vector3.up, normalized);
string executionDetail;
Vector3 val2;
if (fullSentence.Contains("left"))
{
executionDetail = "Left";
val2 = -val + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("right"))
{
executionDetail = "Right";
val2 = val + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("backward") || fullSentence.Contains("backwards") || fullSentence.Contains("back"))
{
executionDetail = "Back";
val2 = -normalized + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("forward") || fullSentence.Contains("forwards"))
{
executionDetail = "Forward";
val2 = normalized + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("up"))
{
executionDetail = "Up";
val2 = Vector3.up;
}
else
{
executionDetail = "Random";
val2 = GetRandomUpwardDirection();
}
base.ExecutionDetail = executionDetail;
((Vector3)(ref val2)).Normalize();
float num = Random.Range(Config.LaunchForceLowerBound.Value, Config.LaunchForceHigherBound.Value);
Vector3 val3 = val2 * num;
player.AddForce(val3, 1f, 1f);
return true;
}
private static Vector3 GetRandomUpwardDirection()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
Vector3 onUnitSphere = Random.onUnitSphere;
onUnitSphere.y = Mathf.Abs(onUnitSphere.y);
if (onUnitSphere.y < 0.5f)
{
onUnitSphere.y = 0.5f;
}
return onUnitSphere;
}
public override void PlayEffects(Vector3 position)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)_cachedLaunchSFX))
{
FindLaunchSFX();
}
if (Object.op_Implicit((Object)(object)_cachedLaunchSFX))
{
GameObject val = Object.Instantiate<GameObject>(_cachedLaunchSFX, position, Quaternion.identity);
val.SetActive(true);
Object.Destroy((Object)(object)val, 5f);
}
}
private static void FindLaunchSFX()
{
ScoutCannon val = Resources.FindObjectsOfTypeAll<ScoutCannon>().FirstOrDefault();
if (val != null)
{
_cachedLaunchSFX = val.fireSFX;
}
}
}
public class SacrificeEvent : VoiceEventBase
{
private readonly HashSet<string> _sacrificeKeywords = VoiceEventBase.ParseKeywords(config.SacrificeKeywords.Value);
private float _lastSacrificeTime = -999f;
public SacrificeEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.SacrificeEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _sacrificeKeywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
if (!Config.SacrificeEnabled.Value)
{
return false;
}
if (player.data.dead)
{
return false;
}
if (Time.time < _lastSacrificeTime + Config.SacrificeCooldown.Value)
{
Debug.Log((object)"[VoiceCurse] Cooldown active, cannot sacrifice now.");
return false;
}
Vector3 deathPos;
Character closestDeadPlayer = DeathTracker.GetClosestDeadPlayer(player.Center, out deathPos);
if (!Object.op_Implicit((Object)(object)closestDeadPlayer))
{
return false;
}
_lastSacrificeTime = Time.time;
Vector3 val = deathPos + Vector3.up * 1f;
base.ExecutionDetail = "Reviving " + closestDeadPlayer.characterName;
closestDeadPlayer.view.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[2] { val, true });
DeathTracker.RemoveDeath(closestDeadPlayer);
player.DieInstantly();
return true;
}
}
[HarmonyPatch(typeof(Character))]
public static class DeathTracker
{
private static readonly Dictionary<int, Vector3> DeathLocations = new Dictionary<int, Vector3>();
[HarmonyPatch("RPCA_Die")]
[HarmonyPrefix]
private static void OnDiePrefix(Character __instance)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
if (Object.op_Implicit((Object)(object)__instance))
{
DeathLocations[((MonoBehaviourPun)__instance).photonView.ViewID] = __instance.Center;
}
}
[HarmonyPatch("RPCA_Revive")]
[HarmonyPostfix]
private static void OnRevivePostfix(Character __instance)
{
RemoveDeath(__instance);
}
[HarmonyPatch("RPCA_ReviveAtPosition")]
[HarmonyPostfix]
private static void OnReviveAtPosPostfix(Character __instance)
{
RemoveDeath(__instance);
}
public static void RemoveDeath(Character c)
{
if (Object.op_Implicit((Object)(object)c) && Object.op_Implicit((Object)(object)((MonoBehaviourPun)c).photonView))
{
DeathLocations.Remove(((MonoBehaviourPun)c).photonView.ViewID);
}
}
public static Character? GetClosestDeadPlayer(Vector3 origin, out Vector3 deathPos)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
Character result = null;
float num = float.MaxValue;
deathPos = Vector3.zero;
foreach (Character item in Character.AllCharacters.Where((Character c) => c.data.dead))
{
Vector3 val;
if (DeathLocations.TryGetValue(((MonoBehaviourPun)item).photonView.ViewID, out var value))
{
val = value;
}
else
{
if (!Object.op_Implicit((Object)(object)item.Ghost))
{
continue;
}
val = ((Component)item.Ghost).transform.position;
}
float num2 = Vector3.Distance(origin, val);
if (num2 < num)
{
num = num2;
result = item;
deathPos = val;
}
}
return result;
}
}
public class SleepEvent : VoiceEventBase
{
private readonly HashSet<string> _sleepKeywords = VoiceEventBase.ParseKeywords(config.SleepKeywords.Value);
public SleepEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.SleepEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _sleepKeywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
if (!Config.SleepEnabled.Value)
{
return false;
}
if (player.data.passedOut || player.data.dead)
{
return false;
}
player.PassOutInstantly();
return true;
}
}
public class SlipEvent : VoiceEventBase
{
private readonly HashSet<string> _keywords = VoiceEventBase.ParseKeywords(config.SlipKeywords.Value);
private static AudioClip? _cachedTripSound;
private Item? _cachedBananaItem;
private static readonly Regex NameCleaner = new Regex("\\s*\\((\\d+|Clone)\\)", RegexOptions.Compiled);
public SlipEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.SlipEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _keywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
if (!Config.SlipEnabled.Value)
{
return false;
}
if (player.data.dead || player.data.fullyPassedOut)
{
return false;
}
if (Config.BananaBombEnabled.Value && Random.value <= Config.BananaBombChance.Value)
{
base.ExecutionDetail = "BananaBomb";
}
player.Fall(Config.SlipStunDuration.Value, 0f);
Vector3 lookDirection_Flat = player.data.lookDirection_Flat;
ApplyForceToPart(player, (BodypartType)16, (lookDirection_Flat + Vector3.up) * 200f);
ApplyForceToPart(player, (BodypartType)13, (lookDirection_Flat + Vector3.up) * 200f);
ApplyForceToPart(player, (BodypartType)0, Vector3.up * 1500f);
ApplyForceToPart(player, (BodypartType)4, lookDirection_Flat * -300f);
return true;
}
private static void ApplyForceToPart(Character player, BodypartType partType, Vector3 force)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
Rigidbody bodypartRig = player.GetBodypartRig(partType);
if (Object.op_Implicit((Object)(object)bodypartRig))
{
bodypartRig.AddForce(force, (ForceMode)1);
}
}
public override void PlayEffects(Character origin, Vector3 position, string detail)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
base.PlayEffects(origin, position, detail);
if (detail == "BananaBomb")
{
SpawnBananaBomb(position);
}
}
public override void PlayEffects(Vector3 position)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
AudioClip tripSound = GetTripSound();
if (Object.op_Implicit((Object)(object)tripSound))
{
AudioSource.PlayClipAtPoint(tripSound, position);
}
}
private void SpawnBananaBomb(Vector3 origin)
{
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_0127: Unknown result type (might be due to invalid IL or missing references)
if (!PhotonNetwork.IsMasterClient)
{
return;
}
Item bananaItem = GetBananaItem();
if (!Object.op_Implicit((Object)(object)bananaItem))
{
if (Config.EnableDebugLogs.Value)
{
Debug.LogWarning((object)"[VoiceCurse] Could not find 'Berrynana Peel Yellow' for Banana Bomb.");
}
return;
}
string text = NameCleaner.Replace(((Object)bananaItem).name, "").Trim();
string text2 = "0_Items/" + text;
int value = Config.BananaBombAmount.Value;
PhotonView val3 = default(PhotonView);
Rigidbody val4 = default(Rigidbody);
for (int i = 0; i < value; i++)
{
Vector3 val = origin + Random.insideUnitSphere * 3f;
val.y = origin.y + 1.5f;
GameObject val2 = PhotonNetwork.Instantiate(text2, val, Quaternion.identity, (byte)0, (object[])null);
if (Object.op_Implicit((Object)(object)val2) && val2.TryGetComponent<PhotonView>(ref val3))
{
val3.RPC("SetKinematicRPC", (RpcTarget)0, new object[3]
{
false,
val,
Quaternion.identity
});
Vector3 randomUpwardDirection = GetRandomUpwardDirection();
float num = Random.Range(25f, 200f);
if (val2.TryGetComponent<Rigidbody>(ref val4))
{
val4.isKinematic = false;
val4.AddForce(randomUpwardDirection * num, (ForceMode)1);
}
}
}
}
private static Vector3 GetRandomUpwardDirection()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
Vector3 onUnitSphere = Random.onUnitSphere;
onUnitSphere.y = Mathf.Abs(onUnitSphere.y);
if (onUnitSphere.y < 0.2f)
{
onUnitSphere.y = 0.5f;
}
return ((Vector3)(ref onUnitSphere)).normalized;
}
private Item? GetBananaItem()
{
if (Object.op_Implicit((Object)(object)_cachedBananaItem))
{
return _cachedBananaItem;
}
_cachedBananaItem = ((IEnumerable<Item>)Resources.FindObjectsOfTypeAll<Item>()).FirstOrDefault((Func<Item, bool>)((Item i) => ((Object)i).name.Contains("Berrynana Peel Yellow")));
return _cachedBananaItem;
}
private static AudioClip? GetTripSound()
{
if (!Object.op_Implicit((Object)(object)_cachedTripSound))
{
_cachedTripSound = ((IEnumerable<AudioClip>)Resources.FindObjectsOfTypeAll<AudioClip>()).FirstOrDefault((Func<AudioClip, bool>)((AudioClip c) => ((Object)c).name == "Au_Slip1"));
}
return _cachedTripSound;
}
}
[Serializable]
public class TransmutePayload
{
public string? ruleName;
public bool isDeath;
public int spawnCount;
public Vector3 deathPosition;
}
public class TransmuteEvent : VoiceEventBase
{
private readonly List<(string Name, string[] Triggers, string[] Targets, Func<bool> IsEnabled)> _definitions;
private readonly Dictionary<string, (string Name, string[] Targets, Func<bool> IsEnabled)> _transmuteLookup = new Dictionary<string, (string, string[], Func<bool>)>();
private readonly Dictionary<string, Item?> _itemCache = new Dictionary<string, Item>();
private static readonly Regex NameCleaner = new Regex("\\s*\\((\\d+|Clone)\\)", RegexOptions.Compiled);
public TransmuteEvent(Config config)
{
Config config2 = config;
base..ctor(config2);
_definitions = new List<(string, string[], string[], Func<bool>)>(9)
{
("Milk", new string[2] { "milk", "calcium" }, new string[1] { "Fortified Milk" }, () => config2.TransmuteMilkEnabled.Value),
("Cactus", new string[2] { "cactus", "cacti" }, new string[1] { "Cactus" }, () => config2.TransmuteCactusEnabled.Value),
("Coconut", new string[1] { "coconut" }, new string[1] { "Coconut" }, () => config2.TransmuteCoconutEnabled.Value),
("Apple", new string[3] { "apple", "berry", "berries" }, new string[3] { "Red Crispberry", "Yellow Crispberry", "Green Crispberry" }, () => config2.TransmuteAppleEnabled.Value),
("Banana", new string[1] { "banana" }, new string[1] { "Berrynana Peel Yellow" }, () => config2.TransmuteBananaEnabled.Value),
("Egg", new string[1] { "egg" }, new string[1] { "Egg" }, () => config2.TransmuteEggEnabled.Value),
("Fruit", new string[1] { "fruit" }, new string[10] { "Red Crispberry", "Yellow Crispberry", "Green Crispberry", "Kingberry Purple", "Kingberry Yellow", "Kingberry Green", "Berrynana Brown", "Berrynana Yellow", "Berrynana Pink", "Berrynana Blue" }, () => config2.TransmuteFruitEnabled.Value),
("Mushroom", new string[5] { "fungus", "mushroom", "fungi", "funghi", "shroom" }, new string[1] { "Mushroom Normie" }, () => config2.TransmuteMushroomEnabled.Value),
("Ball", new string[1] { "ball" }, new string[1] { "Basketball" }, () => config2.TransmuteBallEnabled.Value)
};
foreach (var definition in _definitions)
{
string[] item = definition.Triggers;
foreach (string key in item)
{
_transmuteLookup[key] = (definition.Name, definition.Targets, definition.IsEnabled);
}
}
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.TransmuteEnabled.Value)
{
return Array.Empty<string>();
}
return _definitions.Where<(string, string[], string[], Func<bool>)>(((string Name, string[] Triggers, string[] Targets, Func<bool> IsEnabled) d) => d.IsEnabled()).SelectMany<(string, string[], string[], Func<bool>), string>(((string Name, string[] Triggers, string[] Targets, Func<bool> IsEnabled) d) => d.Triggers);
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
string fullSentence2 = fullSentence;
if (!Config.TransmuteEnabled.Value)
{
return false;
}
if (player.data.dead)
{
return false;
}
string[] array = null;
string text = null;
if (_transmuteLookup.TryGetValue(matchedKeyword, out (string, string[], Func<bool>) value) && value.Item3())
{
(text, array, _) = value;
}
if (array == null)
{
string text2 = _transmuteLookup.Keys.FirstOrDefault((string k) => fullSentence2.Contains(k) && _transmuteLookup[k].IsEnabled());
if (text2 != null)
{
(text, array, _) = _transmuteLookup[text2];
}
}
if (array == null || array.Length == 0)
{
return false;
}
bool value2 = Config.TransmuteDeathEnabled.Value;
TransmutePayload transmutePayload = new TransmutePayload
{
ruleName = text,
isDeath = value2,
spawnCount = 0,
deathPosition = player.Center
};
if (value2)
{
int spawnCount = ClearInventoryForDeath(player);
player.DieInstantly();
transmutePayload.spawnCount = spawnCount;
}
base.ExecutionDetail = text;
base.ExecutionPayload = JsonUtility.ToJson((object)transmutePayload);
return true;
}
public override void PlayEffects(Character origin, Vector3 position, string detail)
{
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
if (!PhotonNetwork.IsMasterClient || !Object.op_Implicit((Object)(object)origin) || string.IsNullOrEmpty(detail))
{
return;
}
TransmutePayload payload;
try
{
payload = JsonUtility.FromJson<TransmutePayload>(detail);
}
catch
{
return;
}
if (payload == null || string.IsNullOrEmpty(payload.ruleName))
{
return;
}
(string, string[], string[], Func<bool>) tuple = _definitions.FirstOrDefault<(string, string[], string[], Func<bool>)>(((string Name, string[] Triggers, string[] Targets, Func<bool> IsEnabled) d) => d.Name == payload.ruleName);
if (tuple.Item3 == null || tuple.Item3.Length == 0)
{
return;
}
string[] item = tuple.Item3;
if (payload.isDeath)
{
Vector3 origin2 = ((payload.deathPosition != Vector3.zero) ? payload.deathPosition : origin.Center);
SpawnTransmutedItems(origin2, payload.spawnCount, item);
return;
}
TransmuteInventoryAlive(origin, item);
float num = Random.Range(Config.AfflictionMinPercent.Value, Config.AfflictionMaxPercent.Value);
if (Object.op_Implicit((Object)(object)origin.refs.afflictions))
{
origin.refs.afflictions.AddStatus((STATUSTYPE)0, num, false, true);
}
}
private static int ClearInventoryForDeath(Character player)
{
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
int num = 1;
Vector3 val = default(Vector3);
((Vector3)(ref val))..ctor(0f, -5000f, 0f);
for (byte b = 0; b < 3; b++)
{
ItemSlot itemSlot = player.player.GetItemSlot(b);
if (itemSlot != null && !itemSlot.IsEmpty())
{
num++;
player.refs.items.photonView.RPC("DropItemFromSlotRPC", (RpcTarget)0, new object[2] { b, val });
}
}
ItemSlot itemSlot2 = player.player.GetItemSlot((byte)3);
if (itemSlot2 != null && !itemSlot2.IsEmpty())
{
BackpackData val2 = default(BackpackData);
if (itemSlot2.data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val2))
{
num += val2.FilledSlotCount();
}
num++;
player.refs.items.photonView.RPC("DropItemFromSlotRPC", (RpcTarget)0, new object[2]
{
(byte)3,
val
});
}
if (Object.op_Implicit((Object)(object)player.refs.afflictions))
{
player.refs.afflictions.UpdateWeight();
}
return num;
}
private void TransmuteInventoryAlive(Character player, string[] possibleTargets)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = default(Vector3);
((Vector3)(ref val))..ctor(0f, -5000f, 0f);
Vector3 center = player.Center;
player.refs.items.photonView.RPC("EquipSlotRpc", (RpcTarget)0, new object[2] { -1, -1 });
for (byte b = 0; b < 3; b++)
{
ItemSlot itemSlot = player.player.GetItemSlot(b);
if (itemSlot != null && !itemSlot.IsEmpty())
{
player.refs.items.photonView.RPC("DropItemFromSlotRPC", (RpcTarget)0, new object[2] { b, val });
SpawnTransmutedItems(center, 1, possibleTargets);
}
}
ItemSlot itemSlot2 = player.player.GetItemSlot((byte)3);
if (itemSlot2 != null && !itemSlot2.IsEmpty())
{
int num = 1;
BackpackData val2 = default(BackpackData);
if (itemSlot2.data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val2))
{
num += val2.FilledSlotCount();
}
player.refs.items.photonView.RPC("DropItemFromSlotRPC", (RpcTarget)0, new object[2]
{
(byte)3,
val
});
SpawnTransmutedItems(center, num, possibleTargets);
}
if (Object.op_Implicit((Object)(object)player.refs.afflictions))
{
player.refs.afflictions.UpdateWeight();
}
}
private void SpawnTransmutedItems(Vector3 origin, int count, string[] targets)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < count; i++)
{
SpawnItem(origin, targets, autoPickup: false, null);
}
}
private void SpawnItem(Vector3 origin, string[] targets, bool autoPickup, Character? picker)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
string searchName = targets[Random.Range(0, targets.Length)];
Item orFindItem = GetOrFindItem(searchName);
if (!Object.op_Implicit((Object)(object)orFindItem))
{
return;
}
Vector3 val = origin + Random.insideUnitSphere * 0.5f;
val.y = origin.y + 0.5f;
string text = NameCleaner.Replace(((Object)orFindItem).name, "").Trim();
string text2 = "0_Items/" + text;
GameObject val2 = PhotonNetwork.Instantiate(text2, val, Quaternion.identity, (byte)0, (object[])null);
PhotonView val3 = default(PhotonView);
if (!Object.op_Implicit((Object)(object)val2) || !val2.TryGetComponent<PhotonView>(ref val3))
{
return;
}
val3.RPC("SetKinematicRPC", (RpcTarget)0, new object[3]
{
false,
val,
Quaternion.identity
});
Item val4 = default(Item);
if (autoPickup && Object.op_Implicit((Object)(object)picker) && val2.TryGetComponent<Item>(ref val4))
{
if (picker.refs != null && (Object)(object)picker.refs.items != (Object)null)
{
picker.refs.items.lastEquippedSlotTime = 0f;
}
val4.Interact(picker);
}
}
private Item? GetOrFindItem(string searchName)
{
string searchName2 = searchName;
if (_itemCache.TryGetValue(searchName2, out Item value))
{
if (Object.op_Implicit((Object)(object)value))
{
return value;
}
_itemCache.Remove(searchName2);
}
Item val = ((IEnumerable<Item>)Resources.FindObjectsOfTypeAll<Item>()).FirstOrDefault((Func<Item, bool>)((Item i) => ((Object)i).name.Contains(searchName2) || (i.UIData != null && i.UIData.itemName.Contains(searchName2))));
_itemCache[searchName2] = val;
if (!Object.op_Implicit((Object)(object)val) && Config.EnableDebugLogs.Value)
{
Debug.LogWarning((object)("[VoiceCurse] Could not find item matching '" + searchName2 + "'"));
}
return val;
}
}
public abstract class VoiceEventBase : IVoiceEvent
{
protected readonly Config Config;
private float _lastExecutionTime;
private float Cooldown => Config.GlobalCooldown.Value;
protected string? ExecutionDetail { get; set; }
protected string? ExecutionPayload { get; set; }
protected VoiceEventBase(Config config)
{
Config = config;
_lastExecutionTime = -999f;
base..ctor();
}
protected abstract IEnumerable<string> GetKeywords();
protected abstract bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword);
protected static HashSet<string> ParseKeywords(string configLine)
{
return (from k in configLine.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
select k.Trim().ToLowerInvariant() into k
where !string.IsNullOrWhiteSpace(k)
select k).ToHashSet();
}
public bool TryExecute(string spokenWord, string fullSentence)
{
//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
string spokenWord2 = spokenWord;
string fullSentence2 = fullSentence;
if (Time.time < _lastExecutionTime + Cooldown)
{
return false;
}
string text = GetKeywords().FirstOrDefault((string keyword) => spokenWord2.Contains(keyword) || (fullSentence2.Contains(keyword) && keyword.EndsWith(" " + spokenWord2, StringComparison.OrdinalIgnoreCase)));
if (text == null)
{
return false;
}
Character localCharacter = Character.localCharacter;
if (!Object.op_Implicit((Object)(object)localCharacter) || !((Component)localCharacter).gameObject.activeInHierarchy)
{
return false;
}
ExecutionDetail = null;
ExecutionPayload = null;
bool flag = false;
try
{
flag = OnExecute(localCharacter, spokenWord2, fullSentence2, text);
}
catch (Exception ex)
{
if (Config.EnableDebugLogs.Value)
{
Debug.LogWarning((object)("[VoiceCurse] Failed to execute " + GetType().Name + ": " + ex.Message));
}
}
if (!flag)
{
return false;
}
_lastExecutionTime = Time.time;
if (Config.EnableDebugLogs.Value)
{
Debug.Log((object)("[VoiceCurse] " + GetType().Name + " executed locally. Broadcasting event..."));
}
string eventName = GetType().Name.Replace("Event", "");
string spokenWord3 = fullSentence2;
int num = fullSentence2.LastIndexOf(text, StringComparison.OrdinalIgnoreCase);
if (num != -1)
{
int num2 = num;
int i = num + text.Length;
while (num2 > 0 && fullSentence2[num2 - 1] != ' ')
{
num2--;
}
for (; i < fullSentence2.Length && fullSentence2[i] != ' '; i++)
{
}
spokenWord3 = fullSentence2.Substring(num2, i - num2);
}
NetworkHandler.SendCurseEvent(spokenWord3, text, eventName, ExecutionDetail, ExecutionPayload, localCharacter.Center);
return true;
}
public virtual void PlayEffects(Character origin, Vector3 position, string detail)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
PlayEffects(origin, position);
}
public virtual void PlayEffects(Character origin, Vector3 position)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
PlayEffects(position);
}
public virtual void PlayEffects(Vector3 position)
{
}
}
public class ZombifyEvent : VoiceEventBase
{
private readonly HashSet<string> _zombifyKeywords = VoiceEventBase.ParseKeywords(config.ZombifyKeywords.Value);
public ZombifyEvent(Config config)
: base(config)
{
}
protected override IEnumerable<string> GetKeywords()
{
if (!Config.ZombifyEnabled.Value)
{
return Enumerable.Empty<string>();
}
return _zombifyKeywords;
}
protected override bool OnExecute(Character player, string spokenWord, string fullSentence, string matchedKeyword)
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
if (!Config.ZombifyEnabled.Value)
{
return false;
}
if (player.data.dead || player.data.zombified)
{
return false;
}
((MonoBehaviourPun)player).photonView.RPC("RPCA_Zombify", (RpcTarget)0, new object[1] { player.Center });
return true;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}