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.jie65535.VoiceCurse")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+d782a3491609b85ef23bb070dcbc3738bd7cc45a")]
[assembly: AssemblyProduct("com.github.jie65535.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, "启用语音识别的调试日志。");
GlobalCooldown = config.Bind<float>("Global", "GlobalCooldown", 2f, "在另一个语音事件可以被触发之前必须经过的时间(以秒为单位)。");
LaunchEnabled = config.Bind<bool>("Event.Launch", "Enabled", true, "启用发射事件。");
LaunchKeywords = config.Bind<string>("Event.Launch", "Keywords", "发射,飞,升空,火箭,弹射,推进,弹跳,喷射,弹起,左,右,前,后,上,下,冲天,蹦极,弹簧,投掷,抛射,加速,猛冲,升天,弹射器,导弹,太空,轨道", "触发发射事件的关键字列表,用逗号分隔。");
LaunchForceLowerBound = config.Bind<float>("Event.Launch", "ForceLowerBound", 1500f, "发射时施加给玩家的最低力值。");
LaunchForceHigherBound = config.Bind<float>("Event.Launch", "ForceHigherBound", 3000f, "发射时施加给玩家的最高力值。");
LaunchStunDuration = config.Bind<float>("Event.Launch", "StunDuration", 3f, "发射后玩家被眩晕/物理击倒的持续时间(以秒为单位)。");
AfflictionEnabled = config.Bind<bool>("Event.Affliction", "Enabled", true, "启用负面状态事件。");
AfflictionMinPercent = config.Bind<float>("Event.Affliction", "MinPercent", 0.2f, "触发时状态条填充的最小百分比 (0.0 到 1.0)。");
AfflictionMaxPercent = config.Bind<float>("Event.Affliction", "MaxPercent", 0.6f, "触发时状态条填充的最大百分比 (0.0 到 1.0)。");
AfflictionTemperatureSwapEnabled = config.Bind<bool>("Event.Affliction", "TemperatureSwapEnabled", true, "如果启用,尝试通过说出相反的词来摆脱热或冷状态,只会将现有效果切换到新的,并在顶部叠加,以防止滥用。");
AfflictionKeywordsInjury = config.Bind<string>("Event.Affliction", "KeywordsInjury", "受伤,伤害,疼痛,创伤,流血,骨折,扭伤,割伤,淤青,伤口,刺痛,酸痛,痛楚,内伤,外伤,重伤,轻伤,伤痛,痛感,橙色", "触发物理伤害的关键字。");
AfflictionKeywordsHunger = config.Bind<string>("Event.Affliction", "KeywordsHunger", "饿,饥渴,吃饭,食物,进食,食欲,想吃,黄色,营养,进食,用餐,零食,大餐,喂食", "触发饥饿状态的关键字。");
AfflictionKeywordsCold = config.Bind<string>("Event.Affliction", "KeywordsCold", "寒冷,冷,冰冻,冰凉,结冰,霜冻,发抖,哆嗦,打颤,寒颤,刺骨,降温,冰雪,冰川,冰柱,低温,冻僵,蓝色,北极,极地", "触发寒冷状态的关键字。");
AfflictionKeywordsHot = config.Bind<string>("Event.Affliction", "KeywordsHot", "热,火烧,火烤,烫,火焰,夏天,红色,地狱,灼烧,沸腾,烘烤,炙烤,烈火,燃烧,点燃", "触发炎热状态的关键字。");
AfflictionKeywordsPoison = config.Bind<string>("Event.Affliction", "KeywordsPoison", "中毒,毒药,毒素,毒液,恶心,想吐,呕吐,头晕,晕眩,不适,难受,污染,感染,疾病,生病,病症,病毒,紫色,病态,疾患", "触发中毒状态的关键字。");
AfflictionKeywordsSpores = config.Bind<string>("Event.Affliction", "KeywordsSpores", "孢子,真菌,霉菌,菌类,粉红,粉色,发霉,霉变", "触发孢子状态的关键字。");
TransmuteEnabled = config.Bind<bool>("Event.Transmute", "Enabled", true, "启用转换事件。");
TransmuteDeathEnabled = config.Bind<bool>("Event.Transmute", "EnableDeath", false, "启用转换触发时的即时死亡(使用你的身体作为转换材料)。如果禁用,你只会受到伤害(你的身体部分转换)。");
TransmuteMilkEnabled = config.Bind<bool>("Event.Transmute", "EnableMilk", true, "启用 'milk/calcium/奶/钙' -> 奶白金");
TransmuteCactusEnabled = config.Bind<bool>("Event.Transmute", "EnableCactus", true, "启用 'cactus/仙人掌' -> 仙人球");
TransmuteCoconutEnabled = config.Bind<bool>("Event.Transmute", "EnableCoconut", true, "启用 'coconut/椰子' -> 椰子");
TransmuteAppleEnabled = config.Bind<bool>("Event.Transmute", "EnableApple", true, "启用 'apple/berry/苹果/浆果' -> 脆莓");
TransmuteBananaEnabled = config.Bind<bool>("Event.Transmute", "EnableBanana", true, "启用 'banana/香蕉' -> 莓蕉皮");
TransmuteEggEnabled = config.Bind<bool>("Event.Transmute", "EnableEgg", true, "启用 'egg/鸡蛋' -> 煎蛋");
TransmuteFruitEnabled = config.Bind<bool>("Event.Transmute", "EnableFruit", true, "启用 'fruit/水果' -> 随机水果");
TransmuteMushroomEnabled = config.Bind<bool>("Event.Transmute", "EnableMushroom", true, "启用 'mushroom/fungus/蘑菇/真菌' -> 蘑菇");
TransmuteBallEnabled = config.Bind<bool>("Event.Transmute", "EnableBall", true, "启用 'ball/球' -> 篮球");
DeathEnabled = config.Bind<bool>("Event.Death", "Enabled", false, "启用死亡事件。");
DeathKeywords = config.Bind<string>("Event.Death", "Keywords", "死亡,去世,毙命,自杀,杀死,骷髅,骨头,尸体,棺材,坟墓,安息,永别,完蛋,嗝屁,牺牲,阵亡,挂了,断气,升天,投胎,终结,末日,死神,坟墓,墓穴,葬送,毁灭,消除,终结", "触发死亡事件的关键字列表,用逗号分隔。");
ZombifyEnabled = config.Bind<bool>("Event.Zombify", "Enabled", true, "启用僵尸化事件。");
ZombifyKeywords = config.Bind<string>("Event.Zombify", "Keywords", "僵尸,丧尸,感染,病毒,瘟疫,行尸,咬,脑子,腐烂,变异,不死,末日,生化,尸变,感染者,丧尸化,僵尸化,行尸走肉,活死人,亡灵,丧尸病毒,爆发,食人,血肉", "触发僵尸化事件的关键字列表,用逗号分隔。");
SleepEnabled = config.Bind<bool>("Event.Sleep", "Enabled", true, "启用睡眠事件。");
SleepKeywords = config.Bind<string>("Event.Sleep", "Keywords", "睡觉,睡眠,疲惫,疲倦,打盹,小睡,休息,昏迷,昏倒,做梦,床,晚安,睡意,困倦,打瞌睡,昏睡,沉睡,熟睡,打哈欠,眼皮重,想睡,睡一觉,眯一会,躺平,打瞌睡,午睡,打盹儿", "触发睡眠事件的关键字列表,用逗号分隔。");
DropEnabled = config.Bind<bool>("Event.Drop", "Enabled", true, "启用掉落事件。");
DropKeywords = config.Bind<string>("Event.Drop", "Keywords", "掉落,丢下,扔掉,失手,滑落,放手,放弃,丢失,手滑,没拿住,掉了,落下了,忘拿了,不见了,失踪,抛弃,丢弃,脱手,撒手,失物,遗失", "触发掉落事件的关键字列表,用逗号分隔。");
ExplodeEnabled = config.Bind<bool>("Event.Explode", "Enabled", true, "启用爆炸事件。");
ExplodeKeywords = config.Bind<string>("Event.Explode", "Keywords", "爆炸,引爆,炸弹,炸药,爆破,爆裂,核弹,手榴弹,地雷,自爆,炸裂,boom,爆破,引爆,爆炸物,炸开,炸成灰,炸成碎片", "触发爆炸事件的关键字列表,用逗号分隔。");
ExplodeRadius = config.Bind<float>("Event.Explode", "Radius", 6f, "爆炸效果和伤害的半径。");
ExplodeDamage = config.Bind<float>("Event.Explode", "DamagePercent", 0.4f, "施加给玩家的伤害百分比 (0.0 到 1.0)。");
ExplodeStunDuration = config.Bind<float>("Event.Explode", "StunDuration", 3f, "爆炸后玩家被眩晕/物理击倒的持续时间(以秒为单位)。");
ExplodeForceLowerBound = config.Bind<float>("Event.Explode", "ForceLowerBound", 2000f, "施加的最低爆炸力值。");
ExplodeForceHigherBound = config.Bind<float>("Event.Explode", "ForceHigherBound", 3000f, "施加的最高爆炸力值。");
SlipEnabled = config.Bind<bool>("Event.Slip", "Enabled", true, "启用滑倒事件。");
SlipKeywords = config.Bind<string>("Event.Slip", "Keywords", "甲沟炎,滑倒,摔倒,绊倒,跌倒,摔跤,失足,溜滑,扑街,摔个狗吃屎,四脚朝天,人仰马翻,失衡,不稳,倾斜,打滑,滑行,绊脚,跌跤", "触发滑倒事件的关键字列表,用逗号分隔。");
SlipStunDuration = config.Bind<float>("Event.Slip", "StunDuration", 2f, "滑倒后玩家被眩晕/物理击倒的持续时间(以秒为单位)。");
BananaBombEnabled = config.Bind<bool>("Event.Slip", "BananaBombEnabled", true, "启用罕见的香蕉炸弹彩蛋。");
BananaBombChance = config.Bind<float>("Event.Slip", "BananaBombChance", 0.05f, "滑倒时触发香蕉炸弹的几率 (0.0 - 1.0)。");
BananaBombAmount = config.Bind<int>("Event.Slip", "BananaBombAmount", 30, "香蕉炸弹生成的香蕉数量。");
SacrificeEnabled = config.Bind<bool>("Event.Sacrifice", "Enabled", true, "启用献祭事件。");
SacrificeKeywords = config.Bind<string>("Event.Sacrifice", "Keywords", "献祭,交易,复活,重生,牺牲,救赎,召唤,归来,复苏,穿越,请神", "触发献祭事件的关键字列表,用逗号分隔。");
SacrificeCooldown = config.Bind<float>("Event.Sacrifice", "Cooldown", 300f, "献祭事件的冷却时间(秒)。");
BlindEnabled = config.Bind<bool>("Event.Blind", "Enabled", true, "启用致盲事件。");
BlindKeywords = config.Bind<string>("Event.Blind", "Keywords", "致盲,闪光,眼睛,眼瞎,视力,看不见,失明,眼花,瞎了", "触发致盲事件的关键字列表,用逗号分隔。");
BlindDuration = config.Bind<float>("Event.Blind", "Duration", 15f, "玩家致盲的持续时间(秒)。");
}
}
[BepInPlugin("com.github.jie65535.VoiceCurse", "VoiceCurse", "1.1.0")]
public class Plugin : BaseUnityPlugin
{
private Config? _config;
private VoiceHandler? _voiceHandler;
private Harmony? _harmony;
public const string Id = "com.github.jie65535.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.jie65535.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 + " 说了 \"" + text2 + "\" 触发了 </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-zh-cn");
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_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Expected I4, but got Unknown
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Invalid comparison between Unknown and I4
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Invalid comparison between Unknown and I4
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: 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 = (int)value switch
{
0 => "受伤",
1 => "饥饿",
2 => "寒冷",
8 => "灼烧",
3 => "中毒",
10 => "孢子",
_ => ((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
{
[CompilerGenerated]
private sealed class <BlindnessRoutine>d__6 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Character player;
public BlindEvent <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <BlindnessRoutine>d__6(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
BlindEvent blindEvent = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if (Time.time < blindEvent._blindEndTime && Object.op_Implicit((Object)(object)player) && !player.data.dead)
{
<>2__current = null;
<>1__state = 1;
return true;
}
if (Object.op_Implicit((Object)(object)player))
{
ToggleBlindness(player, enable: false);
}
blindEvent._blindRoutine = null;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
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;
}
[IteratorStateMachine(typeof(<BlindnessRoutine>d__6))]
private IEnumerator BlindnessRoutine(Character player)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <BlindnessRoutine>d__6(0)
{
<>4__this = this,
player = player
};
}
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_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_0207: Unknown result type (might be due to invalid IL or missing references)
//IL_020c: Unknown result type (might be due to invalid IL or missing references)
//IL_020f: 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)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0176: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
//IL_0186: Unknown result type (might be due to invalid IL or missing references)
//IL_018b: 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_01c0: Unknown result type (might be due to invalid IL or missing references)
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
//IL_01ce: 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") || fullSentence.Contains("左"))
{
executionDetail = "左";
val2 = -val + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("right") || fullSentence.Contains("右"))
{
executionDetail = "右";
val2 = val + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("backward") || fullSentence.Contains("backwards") || fullSentence.Contains("back") || fullSentence.Contains("后"))
{
executionDetail = "后";
val2 = -normalized + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("forward") || fullSentence.Contains("forwards") || fullSentence.Contains("前"))
{
executionDetail = "前";
val2 = normalized + Vector3.up * 0.2f;
}
else if (fullSentence.Contains("up") || fullSentence.Contains("上") || fullSentence.Contains("飞"))
{
executionDetail = "上";
val2 = Vector3.up;
}
else
{
executionDetail = "随机";
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 = "复活 " + 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)
{
("奶", new string[4] { "milk", "calcium", "奶", "钙" }, new string[1] { "Fortified Milk" }, () => config2.TransmuteMilkEnabled.Value),
("仙人掌", new string[3] { "cactus", "cacti", "仙人掌" }, new string[1] { "Cactus" }, () => config2.TransmuteCactusEnabled.Value),
("椰子", new string[2] { "coconut", "椰子" }, new string[1] { "Coconut" }, () => config2.TransmuteCoconutEnabled.Value),
("苹果/浆果", new string[5] { "apple", "berry", "berries", "苹果", "浆果" }, new string[3] { "Red Crispberry", "Yellow Crispberry", "Green Crispberry" }, () => config2.TransmuteAppleEnabled.Value),
("香蕉", new string[2] { "banana", "香蕉" }, new string[1] { "Berrynana Peel Yellow" }, () => config2.TransmuteBananaEnabled.Value),
("鸡蛋", new string[2] { "egg", "鸡蛋" }, new string[1] { "Egg" }, () => config2.TransmuteEggEnabled.Value),
("水果", new string[2] { "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),
("蘑菇", new string[7] { "fungus", "mushroom", "fungi", "funghi", "shroom", "蘑菇", "真菌" }, new string[1] { "Mushroom Normie" }, () => config2.TransmuteMushroomEnabled.Value),
("球", new string[2] { "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_00ee: 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_00e1: 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;
int num = 0;
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 });
num++;
}
}
ItemSlot itemSlot2 = player.player.GetItemSlot((byte)3);
if (itemSlot2 != null && !itemSlot2.IsEmpty())
{
num++;
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 SpawnAndPickupItem(Character player, string[] targets, Vector3 origin)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
SpawnItem(origin, targets, autoPickup: true, player);
}
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)
{
}
}
}