Decompiled source of H3TVR v2.0.0
H3TVR.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using UnityEngine; using UnityEngine.UI; using Valve.VR; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] 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; } } } namespace H3TVR { public class AirdropManager : MonoBehaviour { [CompilerGenerated] private sealed class <>c__DisplayClass7_0 { public GameObject crateGO; internal bool <AirdropSequence>b__0() { //IL_0019: 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) return (Object)(object)crateGO == (Object)null || crateGO.transform.position.y < ((Component)GM.CurrentPlayerBody).transform.position.y + 2f; } } [CompilerGenerated] private sealed class <AirdropSequence>d__7 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public AirdropManager <>4__this; private <>c__DisplayClass7_0 <>8__1; private Vector3 <spawnPos>5__2; private FVRObject <crateTemplate>5__3; private Rigidbody <rb>5__4; private bool <isHelpful>5__5; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AirdropSequence>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <crateTemplate>5__3 = null; <rb>5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0080: 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) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass7_0(); if ((Object)(object)GM.CurrentPlayerBody == (Object)null) { ManualLogSource logger2 = <>4__this.logger; if (logger2 != null) { logger2.LogError((object)"Cannot start airdrop: Player body not found."); } return false; } <spawnPos>5__2 = ((Component)GM.CurrentPlayerBody).transform.position + Vector3.up * 40f; if (!IM.OD.ContainsKey("Crate_Wood_1")) { ManualLogSource logger3 = <>4__this.logger; if (logger3 != null) { logger3.LogError((object)"Cannot start airdrop: Crate template 'Crate_Wood_1' not found."); } return false; } <crateTemplate>5__3 = IM.OD["Crate_Wood_1"]; <>8__1.crateGO = Object.Instantiate<GameObject>(((AnvilAsset)<crateTemplate>5__3).GetGameObject(), <spawnPos>5__2, Quaternion.identity); <rb>5__4 = <>8__1.crateGO.GetComponent<Rigidbody>(); if ((Object)(object)<rb>5__4 == (Object)null) { ManualLogSource logger4 = <>4__this.logger; if (logger4 != null) { logger4.LogError((object)"Airdrop crate has no Rigidbody!"); } Object.Destroy((Object)(object)<>8__1.crateGO); return false; } <rb>5__4.drag = 0.2f; <isHelpful>5__5 = Random.value < 0.7f; <>2__current = (object)new WaitUntil((Func<bool>)(() => (Object)(object)<>8__1.crateGO == (Object)null || <>8__1.crateGO.transform.position.y < ((Component)GM.CurrentPlayerBody).transform.position.y + 2f)); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)<>8__1.crateGO != (Object)null) { <>4__this.PopulateCrate(<>8__1.crateGO.transform.position, <isHelpful>5__5); <>8__1.crateGO.SendMessage("Damage", (object)1000f, (SendMessageOptions)1); ManualLogSource logger = <>4__this.logger; if (logger != null) { logger.LogInfo((object)"Airdrop has landed!"); } } 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 H3TVRImproved plugin; private ManualLogSource logger; private const string CrateId = "Crate_Wood_1"; private const float SpawnHeight = 40f; private const float ParachuteSlowdown = 0.2f; public void Initialize(H3TVRImproved pluginInstance, ManualLogSource logSource) { plugin = pluginInstance; logger = logSource; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Airdrop Manager initialized."); } } public void CallAirdrop(string username) { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Airdrop called in by " + username + "!")); } ((MonoBehaviour)this).StartCoroutine(AirdropSequence()); } private IEnumerator AirdropSequence() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AirdropSequence>d__7(0) { <>4__this = this }; } private void PopulateCrate(Vector3 position, bool isHelpful) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_002d: 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_003c: Unknown result type (might be due to invalid IL or missing references) if (isHelpful) { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Airdrop is... HELPFUL!"); } SpawnRandomHelpfulItem(position); SpawnItem("Health_Sausage", position + Vector3.up * 0.1f); } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"Airdrop is... a TROLL!"); } SpawnItem("PinnedGrenadeM67", position); } } private void SpawnRandomHelpfulItem(Vector3 position) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) string[] array = new string[3] { "MeatBatBaseball", "Health_Sausage", "SuppressorBottle" }; string itemId = array[Random.Range(0, array.Length)]; SpawnItem(itemId, position); } private void SpawnItem(string itemId, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { if (IM.OD.ContainsKey(itemId)) { FVRObject val = IM.OD[itemId]; Object.Instantiate<GameObject>(((AnvilAsset)val).GetGameObject(), position, Quaternion.identity); return; } ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Item '" + itemId + "' not found in ObjectDictionary")); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Failed to spawn item '" + itemId + "': " + ex.Message)); } } } } public class SosigBehaviorController { private ManualLogSource logger; private static readonly LayerMask EnvironmentMask = LayerMask.op_Implicit(LayerMask.GetMask(new string[1] { "Environment" })); public float AllyFollowDistance { get; set; } = 5f; public float AllyMinDistance { get; set; } = 2f; public float AllyCombatRange { get; set; } = 15f; public float AllyDefendRadius { get; set; } = 10f; public bool AllyProtectPlayer { get; set; } = true; public bool AllyHoldFire { get; set; } = false; public void Initialize(ManualLogSource logSource) { logger = logSource; } public void SetupAllyBehavior(Sosig sosig) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) try { FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; object obj; if (currentPlayerBody == null) { obj = null; } else { Transform head = currentPlayerBody.Head; obj = ((head != null) ? ((Component)head).transform : null); } if ((Object)obj == (Object)null) { return; } sosig.E.IFFCode = 0; sosig.SetIFF(0); if (sosig.Priority.IFFChart != null) { for (int i = 0; i < sosig.Priority.IFFChart.Length; i++) { sosig.Priority.IFFChart[i] = i != 0; } } Vector3 position = ((Component)GM.CurrentPlayerBody.Head).transform.position; float num = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f); float num2 = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(position.x + num, position.y, position.z + num2); sosig.CommandAssaultPoint(val); sosig.FallbackOrder = (SosigOrder)4; ConfigureAllyFireSafety(sosig); ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"Ally sosig configured with IFF 0 (player-friendly)"); } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogError((object)("Ally behavior setup failed: " + ex.Message)); } } } private void ConfigureAllyFireSafety(Sosig sosig) { try { sosig.Mustard = 1f; } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Fire safety config warning: " + ex.Message)); } } } public void SetupEnemyBehavior(Sosig sosig) { //IL_0088: 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) try { FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)((currentPlayerBody != null) ? ((Component)currentPlayerBody).transform : null) == (Object)null) { return; } int num = 1; sosig.E.IFFCode = num; sosig.SetIFF(num); if (sosig.Priority.IFFChart != null) { for (int i = 0; i < sosig.Priority.IFFChart.Length; i++) { sosig.Priority.IFFChart[i] = i == 0; } } sosig.CommandAssaultPoint(((Component)GM.CurrentPlayerBody).transform.position); sosig.FallbackOrder = (SosigOrder)4; } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Enemy behavior setup failed: " + ex.Message)); } } } public void UpdateAllyBehavior(Sosig sosig, float followDistance) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Invalid comparison between Unknown and I4 //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Invalid comparison between Unknown and I4 //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: 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_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GM.CurrentPlayerBody?.Head == (Object)null) { return; } try { if (sosig.E.IFFCode != 0) { sosig.E.IFFCode = 0; sosig.SetIFF(0); } if (!sosig.m_isStunned) { Vector3 position = GM.CurrentPlayerBody.Head.position; float num = Vector3.Distance(position, ((Component)sosig).transform.position); float num2 = Vector3.Distance(position, sosig.m_assaultPoint); if (CheckForNearbyEnemies(sosig, AllyCombatRange) && AllyProtectPlayer) { if ((int)sosig.CurrentOrder != 2) { sosig.SetCurrentOrder((SosigOrder)2); } } else if (num2 > followDistance) { float num3 = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f); float num4 = (float)(Random.Range(0, 2) * 2 - 1) * Random.Range(0.75f, 2.5f); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(position.x + num3, position.y, position.z + num4); if (!Physics.Linecast(position, val, LayerMask.op_Implicit(EnvironmentMask))) { sosig.CommandAssaultPoint(val); } } else if (num < AllyMinDistance) { Vector3 val2 = ((Component)sosig).transform.position - position; Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3 val3 = ((Component)sosig).transform.position + normalized * 2f; sosig.CommandAssaultPoint(val3); } } if (sosig.Priority.HasFreshTarget() && (int)sosig.CurrentOrder == 3 && sosig.m_entityRecognition >= 0.65f && IsValidEnemyTarget(sosig)) { sosig.SetCurrentOrder((SosigOrder)2); } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Ally update warning: " + ex.Message)); } } } private bool CheckForNearbyEnemies(Sosig allySosig, float range) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_007c: 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) try { if (allySosig.Priority.HasFreshTarget()) { return true; } Sosig[] array = Object.FindObjectsOfType<Sosig>(); Sosig[] array2 = array; foreach (Sosig val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)allySosig) && (int)val.BodyState != 3 && val.E.IFFCode != 0) { float num = Vector3.Distance(((Component)allySosig).transform.position, ((Component)val).transform.position); if (num <= range) { return true; } } } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Enemy check warning: " + ex.Message)); } } return false; } private bool IsValidEnemyTarget(Sosig sosig) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references) try { FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)((currentPlayerBody != null) ? ((Component)currentPlayerBody).transform : null) == (Object)null) { return false; } Vector3 position = ((Component)GM.CurrentPlayerBody).transform.position; Vector3 position2 = ((Component)sosig).transform.position; if (sosig.Priority.HasFreshTarget()) { return true; } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Target validation warning: " + ex.Message)); } } return false; } public void UpdateEnemyBehavior(Sosig sosig, float aggressionDistance) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Invalid comparison between Unknown and I4 //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GM.CurrentPlayerBody?.Head == (Object)null) { return; } if (!sosig.m_isStunned) { Vector3 position = GM.CurrentPlayerBody.Head.position; float num = Vector3.Distance(position, ((Component)sosig.Links[1]).transform.position); if (num > aggressionDistance) { sosig.CommandAssaultPoint(((Component)GM.CurrentPlayerBody).transform.position); } } if (sosig.Priority.HasFreshTarget() && (int)sosig.CurrentOrder == 3 && sosig.m_entityRecognition >= 0.55f) { sosig.SetCurrentOrder((SosigOrder)2); } if ((int)sosig.CurrentOrder == 0 || (int)sosig.CurrentOrder == 9 || (int)sosig.CurrentOrder == 1) { sosig.CommandAssaultPoint(((Component)GM.CurrentPlayerBody).transform.position); } } public void SetAlliesHoldFire(bool holdFire) { AllyHoldFire = holdFire; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)$"Allies hold fire: {holdFire}"); } } public void CommandAlliesDefendPoint(Vector3 point) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0073: 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) try { foreach (Sosig spawnedChatter in AdvancedChatSosigSpawner.spawnedChatters) { if ((Object)(object)spawnedChatter != (Object)null && (int)spawnedChatter.BodyState != 3) { spawnedChatter.CommandAssaultPoint(point); spawnedChatter.SetCurrentOrder((SosigOrder)1); } } ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)$"Allies commanded to defend point: {point}"); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Defend point command failed: " + ex.Message)); } } } public void CommandAlliesFollowPlayer() { //IL_002c: 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) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_008c: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)GM.CurrentPlayerBody?.Head == (Object)null) { return; } Vector3 position = GM.CurrentPlayerBody.Head.position; Vector3 val = default(Vector3); foreach (Sosig spawnedChatter in AdvancedChatSosigSpawner.spawnedChatters) { if ((Object)(object)spawnedChatter != (Object)null && (int)spawnedChatter.BodyState != 3) { float num = Random.Range(-2f, 2f); float num2 = Random.Range(-2f, 2f); ((Vector3)(ref val))..ctor(position.x + num, position.y, position.z + num2); spawnedChatter.CommandAssaultPoint(val); spawnedChatter.FallbackOrder = (SosigOrder)4; } } ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Allies commanded to follow player"); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Follow command failed: " + ex.Message)); } } } public void CommandAlliesAttackTarget(Vector3 targetPosition) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0073: 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) try { foreach (Sosig spawnedChatter in AdvancedChatSosigSpawner.spawnedChatters) { if ((Object)(object)spawnedChatter != (Object)null && (int)spawnedChatter.BodyState != 3) { spawnedChatter.CommandAssaultPoint(targetPosition); spawnedChatter.SetCurrentOrder((SosigOrder)7); } } ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)$"Allies commanded to attack: {targetPosition}"); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Attack command failed: " + ex.Message)); } } } } public class SosigNameManager { private List<string> allyNames = new List<string>(); private List<string> enemyNames = new List<string>(); private ManualLogSource logger; public void Initialize(ManualLogSource logSource) { logger = logSource; } public void LoadNameLists(string allyPath, string enemyPath) { try { if (File.Exists(allyPath)) { string[] array = File.ReadAllLines(allyPath); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("#") && !text2.StartsWith(";")) { allyNames.Add(text2); } } ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)$"Loaded {allyNames.Count} ally names"); } } else { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogWarning((object)("Ally names file not found: " + allyPath)); } CreateDefaultNameFile(allyPath, isAlly: true); } if (File.Exists(enemyPath)) { string[] array3 = File.ReadAllLines(enemyPath); string[] array4 = array3; foreach (string text3 in array4) { string text4 = text3.Trim(); if (!string.IsNullOrEmpty(text4) && !text4.StartsWith("#") && !text4.StartsWith(";")) { enemyNames.Add(text4); } } ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)$"Loaded {enemyNames.Count} enemy names"); } } else { ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogWarning((object)("Enemy names file not found: " + enemyPath)); } CreateDefaultNameFile(enemyPath, isAlly: false); } } catch (Exception ex) { ManualLogSource obj5 = logger; if (obj5 != null) { obj5.LogError((object)("Failed to load name lists: " + ex.Message)); } } } private void CreateDefaultNameFile(string path, bool isAlly) { try { string directoryName = Path.GetDirectoryName(path); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } string[] array = ((!isAlly) ? new string[6] { "# Enemy Sosig Names", "Hostile Bot", "Attacker", "Enemy", "Threat", "Opponent" } : new string[6] { "# Ally Sosig Names", "Friendly Bot", "Guardian", "Protector", "Ally", "Helper" }); File.WriteAllLines(path, array); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Created default name file: " + path)); } if (isAlly) { for (int i = 1; i < array.Length; i++) { allyNames.Add(array[i]); } } else { for (int j = 1; j < array.Length; j++) { enemyNames.Add(array[j]); } } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Failed to create default name file: " + ex.Message)); } } } public string GetRandomName(bool isAlly, SteamFriendsIntegration steamFriends = null, bool useSteamFriends = false) { if ((Object)(object)steamFriends != (Object)null && steamFriends.IsAvailable() && useSteamFriends) { try { string randomFriendName = steamFriends.GetRandomFriendName(); if (!string.IsNullOrEmpty(randomFriendName) && randomFriendName != "Steam Friend") { return randomFriendName; } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Failed to get Steam friend name: " + ex.Message)); } } } List<string> list = (isAlly ? allyNames : enemyNames); if (list.Count == 0) { return isAlly ? "Ally" : "Enemy"; } return list[Random.Range(0, list.Count)]; } } public class SosigNameplateManager { public void AttachNameplate(Sosig sosig, string name, GameObject nameplatePrefab, bool isEnemy) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) try { if (sosig.Links.Count != 0 && !((Object)(object)nameplatePrefab == (Object)null)) { GameObject val = Object.Instantiate<GameObject>(nameplatePrefab, ((Component)sosig.Links[0]).transform, false); val.transform.localPosition = new Vector3(0f, 0.3f, 0f); val.transform.localRotation = Quaternion.identity; Text[] componentsInChildren = val.GetComponentsInChildren<Text>(); Text[] array = componentsInChildren; foreach (Text val2 in array) { val2.text = name; } } } catch (Exception) { } } } public class SosigSpawnConfig { public ConfigEntry<int> maxAllySosigs; public ConfigEntry<int> maxEnemySosigs; public ConfigEntry<float> spawnCooldown; public ConfigEntry<bool> enableNameplates; public ConfigEntry<float> sosigLifetime; public ConfigEntry<bool> enableAutoCleanup; public ConfigEntry<float> enemyIFF; public ConfigEntry<float> followDistance; public ConfigEntry<float> enemyAggressionDistance; public ConfigEntry<bool> useModernSpawnSystem; public ConfigEntry<string> allySosigPool; public ConfigEntry<string> enemySosigPool; public ConfigEntry<bool> enableArmorCustomization; public ConfigEntry<string> allyNamesFilePath; public ConfigEntry<string> enemyNamesFilePath; public ConfigEntry<bool> useRandomNames; public ConfigEntry<int> maxSosigsPerUser; public ConfigEntry<bool> enableCoverAI; public ConfigEntry<float> sosigUpdateInterval; public ConfigEntry<bool> enableChatWatcherIntegration; public List<SosigEnemyID> allyPoolIDs = new List<SosigEnemyID>(); public List<SosigEnemyID> enemyPoolIDs = new List<SosigEnemyID>(); public SosigEnemyID defaultAllyID = (SosigEnemyID)100; public SosigEnemyID defaultEnemyID = (SosigEnemyID)108; private ManualLogSource logger; public void Initialize(ConfigFile config, ManualLogSource logSource) { logger = logSource; maxAllySosigs = config.Bind<int>("Chat Spawner", "MaxAllySosigs", 8, "Maximum ally sosigs"); maxEnemySosigs = config.Bind<int>("Chat Spawner", "MaxEnemySosigs", 8, "Maximum enemy sosigs"); spawnCooldown = config.Bind<float>("Chat Spawner", "SpawnCooldown", 2f, "Cooldown between spawns"); enableNameplates = config.Bind<bool>("Chat Spawner", "EnableNameplates", true, "Show nameplates above sosigs"); sosigLifetime = config.Bind<float>("Chat Spawner", "SosigLifetime", 300f, "Sosig lifetime in seconds (0 = infinite)"); enableAutoCleanup = config.Bind<bool>("Chat Spawner", "EnableAutoCleanup", true, "Auto cleanup dead sosigs"); enemyIFF = config.Bind<float>("Chat Spawner", "EnemyIFF", 1f, "Enemy IFF code"); followDistance = config.Bind<float>("Chat Spawner", "FollowDistance", 6f, "Distance for allies to follow player"); enemyAggressionDistance = config.Bind<float>("Chat Spawner", "EnemyAggressionDistance", 20f, "Distance at which enemies become aggressive"); useModernSpawnSystem = config.Bind<bool>("Chat Spawner", "UseModernSpawnSystem", true, "Use Update 120's modern TNH sosig spawn system (recommended)"); allySosigPool = config.Bind<string>("Chat Spawner", "AllySosigPool", "M_Swat_Scout,M_Swat_Sniper,M_Swat_Breacher", "Comma-separated list of SosigEnemyID names for allies"); enemySosigPool = config.Bind<string>("Chat Spawner", "EnemySosigPool", "M_Swat_Heavy,M_Swat_Breacher,M_Swat_Sniper", "Comma-separated list of SosigEnemyID names for enemies"); enableArmorCustomization = config.Bind<bool>("Chat Spawner Advanced", "EnableArmorCustomization", true, "Enable armor customization system"); allyNamesFilePath = config.Bind<string>("Chat Spawner Advanced", "AllyNamesFilePath", "Plugins/H3TwitchTools/AllyNames.ini", "File path for ally names list (INI file)"); enemyNamesFilePath = config.Bind<string>("Chat Spawner Advanced", "EnemyNamesFilePath", "Plugins/H3TwitchTools/EnemyNames.ini", "File path for enemy names list (INI file)"); useRandomNames = config.Bind<bool>("Chat Spawner Advanced", "UseRandomNames", true, "Use random names from name lists"); maxSosigsPerUser = config.Bind<int>("Chat Spawner Advanced", "MaxSosigsPerUser", 2, "Maximum sosigs per Twitch user"); enableCoverAI = config.Bind<bool>("Chat Spawner Advanced", "EnableCoverAI", true, "Enable advanced cover-taking AI behavior"); sosigUpdateInterval = config.Bind<float>("Chat Spawner Advanced", "UpdateInterval", 1f, "Interval between sosig AI updates (seconds)"); enableChatWatcherIntegration = config.Bind<bool>("Chat Spawner Integration", "EnableChatWatcher", true, "Enable ChatWatcher integration for file-based Twitch chat spawning"); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Sosig spawn configuration initialized"); } } public void InitializeSosigPools() { //IL_0042: 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) //IL_004f: 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_00d9: Unknown result type (might be due to invalid IL or missing references) try { string[] array = allySosigPool.Value.Split(new char[1] { ',' }); string[] array2 = array; foreach (string text in array2) { try { SosigEnemyID item = (SosigEnemyID)Enum.Parse(typeof(SosigEnemyID), text.Trim()); allyPoolIDs.Add(item); } catch { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)("Invalid ally sosig ID: " + text)); } } } string[] array3 = enemySosigPool.Value.Split(new char[1] { ',' }); string[] array4 = array3; foreach (string text2 in array4) { try { SosigEnemyID item2 = (SosigEnemyID)Enum.Parse(typeof(SosigEnemyID), text2.Trim()); enemyPoolIDs.Add(item2); } catch { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Invalid enemy sosig ID: " + text2)); } } } if (allyPoolIDs.Count == 0) { allyPoolIDs.Add((SosigEnemyID)100); allyPoolIDs.Add((SosigEnemyID)102); } if (enemyPoolIDs.Count == 0) { enemyPoolIDs.Add((SosigEnemyID)108); enemyPoolIDs.Add((SosigEnemyID)109); } ManualLogSource obj5 = logger; if (obj5 != null) { obj5.LogInfo((object)$"Loaded {allyPoolIDs.Count} ally sosig types, {enemyPoolIDs.Count} enemy sosig types"); } } catch (Exception ex) { ManualLogSource obj6 = logger; if (obj6 != null) { obj6.LogError((object)("Failed to initialize sosig pools: " + ex.Message)); } allyPoolIDs.Add((SosigEnemyID)100); enemyPoolIDs.Add((SosigEnemyID)108); } } public SosigEnemyID GetRandomAllyID() { //IL_0033: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) if (allyPoolIDs.Count == 0) { return defaultAllyID; } return allyPoolIDs[Random.Range(0, allyPoolIDs.Count)]; } public SosigEnemyID GetRandomEnemyID() { //IL_0033: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) if (enemyPoolIDs.Count == 0) { return defaultEnemyID; } return enemyPoolIDs[Random.Range(0, enemyPoolIDs.Count)]; } } public class SosigSpawner { private ManualLogSource logger; private SosigTemplateCache templateCache; public void Initialize(ManualLogSource logSource, SosigTemplateCache cache) { logger = logSource; templateCache = cache; } public Sosig SpawnModern(SosigEnemyID enemyID, Vector3 pos, Quaternion rot, int IFF) { //IL_0008: 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) //IL_0048: 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) try { SosigEnemyTemplate template = templateCache.GetTemplate(enemyID); if ((Object)(object)template == (Object)null) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)$"Could not find template for SosigEnemyID: {enemyID}"); } return null; } Sosig val = SpawnLegacy(template, pos, rot, IFF); if ((Object)(object)val == (Object)null) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)"Modern sosig spawn returned null"); } return null; } try { if (template.ConfigTemplates != null && template.ConfigTemplates.Count > 0) { SosigConfigTemplate val2 = template.ConfigTemplates[Random.Range(0, template.ConfigTemplates.Count)]; val.Configure(val2); } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogWarning((object)("Failed to apply config template: " + ex.Message)); } } int armorLevel = ((IFF == 0) ? SosigCustomizationUI.AllyArmor.Value : SosigCustomizationUI.EnemyArmor.Value); SosigArmorManager.ApplyArmorToSosig(val, armorLevel); val.E.IFFCode = IFF; val.SetIFF(IFF); try { val.Inventory.FillAllAmmo(); } catch (Exception ex2) { ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogWarning((object)("Failed to fill ammo: " + ex2.Message)); } } try { if (template.OutfitConfig != null && template.OutfitConfig.Count > 0) { ApplyOutfit(val, template.OutfitConfig[Random.Range(0, template.OutfitConfig.Count)]); } } catch (Exception ex3) { ManualLogSource obj5 = logger; if (obj5 != null) { obj5.LogWarning((object)("Failed to apply outfit: " + ex3.Message)); } } return val; } catch (Exception ex4) { ManualLogSource obj6 = logger; if (obj6 != null) { obj6.LogError((object)("Modern sosig spawn failed: " + ex4.Message + "\n" + ex4.StackTrace)); } return null; } } public Sosig SpawnLegacy(SosigEnemyTemplate template, Vector3 pos, Quaternion rot, int IFF) { //IL_00a5: 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_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)template == (Object)null || template.SosigPrefabs == null || template.SosigPrefabs.Count == 0) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)"Invalid template"); } return null; } FVRObject val = template.SosigPrefabs[Random.Range(0, template.SosigPrefabs.Count)]; if ((Object)(object)((val != null) ? ((AnvilAsset)val).GetGameObject() : null) == (Object)null) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)"Invalid prefab"); } return null; } GameObject val2 = Object.Instantiate<GameObject>(((AnvilAsset)val).GetGameObject(), pos, rot); Sosig componentInChildren = val2.GetComponentInChildren<Sosig>(); if ((Object)(object)componentInChildren == (Object)null) { Object.Destroy((Object)(object)val2); return null; } if (template.ConfigTemplates != null && template.ConfigTemplates.Count > 0) { SosigConfigTemplate val3 = template.ConfigTemplates[Random.Range(0, template.ConfigTemplates.Count)]; if ((Object)(object)val3 != (Object)null) { componentInChildren.Configure(val3); } } componentInChildren.E.IFFCode = IFF; if (IFF < componentInChildren.Priority.IFFChart.Length) { componentInChildren.Priority.IFFChart[IFF] = true; } int armorLevel = ((IFF == 0) ? SosigCustomizationUI.AllyArmor.Value : SosigCustomizationUI.EnemyArmor.Value); SosigArmorManager.ApplyArmorToSosig(componentInChildren, armorLevel); EquipWeapons(componentInChildren, template, pos, rot); if (template.OutfitConfig != null && template.OutfitConfig.Count > 0) { ApplyOutfit(componentInChildren, template.OutfitConfig[Random.Range(0, template.OutfitConfig.Count)]); } return componentInChildren; } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogError((object)("Legacy sosig spawn failed: " + ex.Message)); } return null; } } private void EquipWeapons(Sosig sosig, SosigEnemyTemplate template, Vector3 pos, Quaternion rot) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_009c: 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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (!SosigCustomizationUI.EnableGuns.Value) { return; } try { if (template.WeaponOptions != null && template.WeaponOptions.Count > 0) { EquipWeapon(sosig, template.WeaponOptions[Random.Range(0, template.WeaponOptions.Count)], pos, rot); } if (template.WeaponOptions_Secondary != null && template.WeaponOptions_Secondary.Count > 0) { EquipWeapon(sosig, template.WeaponOptions_Secondary[Random.Range(0, template.WeaponOptions_Secondary.Count)], pos, rot); } if (template.WeaponOptions_Tertiary != null && template.WeaponOptions_Tertiary.Count > 0) { EquipWeapon(sosig, template.WeaponOptions_Tertiary[Random.Range(0, template.WeaponOptions_Tertiary.Count)], pos, rot); } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Weapon equip failed: " + ex.Message)); } } } private void EquipWeapon(Sosig sosig, FVRObject weaponObj, Vector3 pos, Quaternion rot) { //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) //IL_002e: 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) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)((weaponObj != null) ? ((AnvilAsset)weaponObj).GetGameObject() : null) == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(((AnvilAsset)weaponObj).GetGameObject(), pos + Vector3.up * 0.1f, rot); SosigWeapon component = val.GetComponent<SosigWeapon>(); if ((Object)(object)component != (Object)null) { component.SetAutoDestroy(true); component.O.SpawnLockable = false; component.SetAmmoClamping(true); component.IsShakeReloadable = false; if ((int)component.Type == 0) { sosig.Inventory.FillAmmoWithType(component.AmmoType); } sosig.Inventory.Init(); sosig.Inventory.FillAllAmmo(); sosig.InitHands(); sosig.ForceEquip(component); } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Weapon equip error: " + ex.Message)); } } } private void ApplyOutfit(Sosig sosig, SosigOutfitConfig outfit) { try { if (!((Object)(object)outfit == (Object)null) && sosig.Links.Count >= 4) { if (Random.value < outfit.Chance_Headwear) { SpawnAccessory(outfit.Headwear, sosig.Links[0]); } if (Random.value < outfit.Chance_Facewear) { SpawnAccessory(outfit.Facewear, sosig.Links[0]); } if (Random.value < outfit.Chance_Eyewear) { SpawnAccessory(outfit.Eyewear, sosig.Links[0]); } if (Random.value < outfit.Chance_Torsowear) { SpawnAccessory(outfit.Torsowear, sosig.Links[1]); } if (Random.value < outfit.Chance_Pantswear) { SpawnAccessory(outfit.Pantswear, sosig.Links[2]); } if (sosig.Links.Count > 3 && Random.value < outfit.Chance_Pantswear_Lower) { SpawnAccessory(outfit.Pantswear_Lower, sosig.Links[3]); } if (Random.value < outfit.Chance_Backpacks) { SpawnAccessory(outfit.Backpacks, sosig.Links[1]); } if (Random.value < outfit.Chance_TorosDecoration) { SpawnAccessory(outfit.TorosDecoration, sosig.Links[1]); } } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Outfit apply failed: " + ex.Message)); } } } private void SpawnAccessory(List<FVRObject> accessories, SosigLink link) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (accessories == null || accessories.Count == 0 || (Object)(object)link == (Object)null) { return; } try { FVRObject val = accessories[Random.Range(0, accessories.Count)]; if (!((Object)(object)((val != null) ? ((AnvilAsset)val).GetGameObject() : null) == (Object)null)) { GameObject val2 = Object.Instantiate<GameObject>(((AnvilAsset)val).GetGameObject(), ((Component)link).transform.position, ((Component)link).transform.rotation); val2.transform.SetParent(((Component)link).transform); SosigWearable component = val2.GetComponent<SosigWearable>(); if ((Object)(object)component != (Object)null) { component.RegisterWearable(link); } } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Accessory spawn failed: " + ex.Message)); } } } } public class SosigSpawnPositionCalculator { public Vector3 CalculateAllySpawnPoint() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_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_0099: Unknown result type (might be due to invalid IL or missing references) FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; object obj; if (currentPlayerBody == null) { obj = null; } else { Transform head = currentPlayerBody.Head; obj = ((head != null) ? ((Component)head).transform : null); } if ((Object)obj == (Object)null) { return Vector3.zero; } Vector3 position = ((Component)GM.CurrentPlayerBody.Head).transform.position; float num = Random.Range(0f, 360f) * ((float)Math.PI / 180f); float num2 = Random.Range(2f, 4f); return new Vector3(position.x + Mathf.Cos(num) * num2, position.y, position.z + Mathf.Sin(num) * num2); } public Vector3 CalculateEnemySpawnPoint() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_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_0099: Unknown result type (might be due to invalid IL or missing references) FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; object obj; if (currentPlayerBody == null) { obj = null; } else { Transform head = currentPlayerBody.Head; obj = ((head != null) ? ((Component)head).transform : null); } if ((Object)obj == (Object)null) { return Vector3.zero; } Vector3 position = ((Component)GM.CurrentPlayerBody.Head).transform.position; float num = Random.Range(0f, 360f) * ((float)Math.PI / 180f); float num2 = Random.Range(8f, 15f); return new Vector3(position.x + Mathf.Cos(num) * num2, position.y, position.z + Mathf.Sin(num) * num2); } public Vector3 CalculateBossSpawnPoint() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_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_0099: Unknown result type (might be due to invalid IL or missing references) FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; object obj; if (currentPlayerBody == null) { obj = null; } else { Transform head = currentPlayerBody.Head; obj = ((head != null) ? ((Component)head).transform : null); } if ((Object)obj == (Object)null) { return Vector3.zero; } Vector3 position = ((Component)GM.CurrentPlayerBody.Head).transform.position; float num = Random.Range(0f, 360f) * ((float)Math.PI / 180f); float num2 = Random.Range(20f, 30f); return new Vector3(position.x + Mathf.Cos(num) * num2, position.y, position.z + Mathf.Sin(num) * num2); } } public class SosigTemplateCache { private Dictionary<SosigEnemyID, SosigEnemyTemplate> templateCache = new Dictionary<SosigEnemyID, SosigEnemyTemplate>(); private ManualLogSource logger; public void Initialize(ManualLogSource logSource) { logger = logSource; } public void BuildCache(List<SosigEnemyID> allyPoolIDs, List<SosigEnemyID> enemyPoolIDs) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) //IL_00bb: 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) //IL_00d9: 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) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)ManagerSingleton<IM>.Instance == (Object)null) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)"Cannot build template cache - IM.Instance is null"); } return; } if (ManagerSingleton<IM>.Instance.odicSosigObjsByID == null) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogWarning((object)"Cannot build template cache - odicSosigObjsByID is null"); } return; } int num = 0; ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)"Building template cache from IM.Instance..."); } foreach (SosigEnemyID item in allyPoolIDs.Concat(enemyPoolIDs).Distinct()) { if (ManagerSingleton<IM>.Instance.odicSosigObjsByID.ContainsKey(item)) { SosigEnemyTemplate val = ManagerSingleton<IM>.Instance.odicSosigObjsByID[item]; if ((Object)(object)val != (Object)null) { templateCache[item] = val; num++; ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogDebug((object)$" Cached: {item}"); } } else { ManualLogSource obj5 = logger; if (obj5 != null) { obj5.LogWarning((object)$" Template null for {item}"); } } } else { ManualLogSource obj6 = logger; if (obj6 != null) { obj6.LogWarning((object)$" ID not found in IM: {item}"); } } } ManualLogSource obj7 = logger; if (obj7 != null) { obj7.LogInfo((object)$"Template cache built: {num}/{allyPoolIDs.Count + enemyPoolIDs.Count} templates loaded"); } ManualLogSource obj8 = logger; if (obj8 != null) { obj8.LogInfo((object)$"Template cache status: {templateCache.Count} total templates"); } } catch (Exception ex) { ManualLogSource obj9 = logger; if (obj9 != null) { obj9.LogWarning((object)("Failed to build template cache: " + ex.Message)); } } } public SosigEnemyTemplate GetTemplate(SosigEnemyID enemyID) { //IL_0007: 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_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_0067: 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_008f: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (templateCache.ContainsKey(enemyID)) { return templateCache[enemyID]; } if ((Object)(object)ManagerSingleton<IM>.Instance != (Object)null && ManagerSingleton<IM>.Instance.odicSosigObjsByID != null && ManagerSingleton<IM>.Instance.odicSosigObjsByID.ContainsKey(enemyID)) { SosigEnemyTemplate val = ManagerSingleton<IM>.Instance.odicSosigObjsByID[enemyID]; templateCache[enemyID] = val; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)$"Cached template for {enemyID} from IM.Instance"); } return val; } ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogWarning((object)$"Template not in cache for {enemyID}, searching Resources..."); } SosigEnemyTemplate[] array = Resources.FindObjectsOfTypeAll<SosigEnemyTemplate>(); SosigEnemyTemplate[] array2 = array; foreach (SosigEnemyTemplate val2 in array2) { if ((Object)(object)val2 != (Object)null && val2.SosigEnemyID == enemyID) { templateCache[enemyID] = val2; ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)$"Found and cached template for {enemyID}"); } return val2; } } ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)$"Could not find template for SosigEnemyID: {enemyID}"); } return null; } public bool HasTemplate(SosigEnemyID enemyID) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return templateCache.ContainsKey(enemyID); } public int GetCacheSize() { return templateCache.Count; } } public enum SpawnPriority { Low, Normal, High, Immediate } public class ChatWatcher : MonoBehaviour { private struct ParsedContentResult { public List<string> Usernames; public List<KeyValuePair<string, int>> ArmorCommands; public List<UserArmorCommand> UserArmorCommands; } private struct UserArmorCommand { public string Username; public int ArmorLevel; public string ArmorName; } public struct ChatWatcherStats { public bool FileWatchingActive; public int ProcessedUsernames; public int ActiveAllies; public int ActiveEnemies; public int TotalActiveSosigs; } private H3TVRImproved plugin; private ManualLogSource logger; private AdvancedChatSosigSpawner sosigSpawner; private ConfigEntry<bool> enableFileWatching; private ConfigEntry<string> allyChatFilePath; private ConfigEntry<string> enemyChatFilePath; private ConfigEntry<float> fileCheckInterval; private ConfigEntry<bool> clearFileAfterRead; private ConfigEntry<KeyCode> manualAllySpawnKey; private ConfigEntry<KeyCode> manualEnemySpawnKey; private ConfigEntry<KeyCode> clearAllSosigsKey; private float lastFileCheckTime; private string lastAllyFileContent = ""; private string lastEnemyFileContent = ""; private HashSet<string> processedUsernames = new HashSet<string>(); public static ChatWatcher Instance { get; private set; } public void Initialize(H3TVRImproved pluginInstance, ManualLogSource logSource, AdvancedChatSosigSpawner spawner) { if ((Object)(object)Instance != (Object)null) { Object.Destroy((Object)(object)this); return; } Instance = this; plugin = pluginInstance; logger = logSource; sosigSpawner = spawner; InitializeConfiguration(); if (enableFileWatching.Value) { InitializeFileWatching(); } ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Chat Watcher initialized (H3TwitchTools compatible file mode - Channel Points ready)"); } } private void InitializeConfiguration() { H3TVRImproved h3TVRImproved = plugin; if (((h3TVRImproved != null) ? ((BaseUnityPlugin)h3TVRImproved).Config : null) == null) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)"Plugin config is null"); } return; } try { enableFileWatching = ((BaseUnityPlugin)plugin).Config.Bind<bool>("Chat Watcher - File Mode", "EnableFileWatching", true, "Enable file watching mode for chat integration (H3TwitchTools style)"); allyChatFilePath = ((BaseUnityPlugin)plugin).Config.Bind<string>("Chat Watcher - File Mode", "AllyChatFilePath", "BepInEx/config/H3TVR_AllyChat.txt", "Path to ally chat file\nSUPPORTED FORMATS:\n - Plain username: ViewerName\n - Key=Value: username=ViewerName\n - Key=Value: user=ViewerName\n - Key=Value: redeemer=ViewerName\nSUPPORTS ABSOLUTE PATHS: C:\\StreamFiles\\ally_chat.txt\nOr relative: BepInEx/config/H3TVR_AllyChat.txt"); enemyChatFilePath = ((BaseUnityPlugin)plugin).Config.Bind<string>("Chat Watcher - File Mode", "EnemyChatFilePath", "BepInEx/config/H3TVR_EnemyChat.txt", "Path to enemy chat file\nSUPPORTED FORMATS:\n - Plain username: ViewerName\n - Key=Value: username=ViewerName\n - Key=Value: user=ViewerName\n - Key=Value: redeemer=ViewerName\nSUPPORTS ABSOLUTE PATHS: C:\\StreamFiles\\enemy_chat.txt\nOr relative: BepInEx/config/H3TVR_EnemyChat.txt"); fileCheckInterval = ((BaseUnityPlugin)plugin).Config.Bind<float>("Chat Watcher - File Mode", "FileCheckInterval", 0.5f, "How often to check files for changes (seconds)"); clearFileAfterRead = ((BaseUnityPlugin)plugin).Config.Bind<bool>("Chat Watcher - File Mode", "ClearFileAfterRead", true, "Clear chat file after reading usernames (recommended for channel points)"); manualAllySpawnKey = ((BaseUnityPlugin)plugin).Config.Bind<KeyCode>("Chat Watcher - Keys", "ManualAllySpawnKey", (KeyCode)112, "Key to manually spawn ally sosig"); manualEnemySpawnKey = ((BaseUnityPlugin)plugin).Config.Bind<KeyCode>("Chat Watcher - Keys", "ManualEnemySpawnKey", (KeyCode)111, "Key to manually spawn enemy sosig"); clearAllSosigsKey = ((BaseUnityPlugin)plugin).Config.Bind<KeyCode>("Chat Watcher - Keys", "ClearAllSosigsKey", (KeyCode)127, "Key to clear all chat sosigs"); ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)"Chat Watcher configuration initialized (Channel Points format supported)"); } } catch (Exception ex) { ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogError((object)("Chat Watcher config init failed: " + ex.Message)); } } } private void InitializeFileWatching() { try { string text = ResolveFilePath(allyChatFilePath.Value); CreateFileIfNotExists(text, isAlly: true); string text2 = ResolveFilePath(enemyChatFilePath.Value); CreateFileIfNotExists(text2, isAlly: false); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"File watching initialized (Channel Points ready)"); } ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)(" Ally file: " + text)); } ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)(" Enemy file: " + text2)); } } catch (Exception ex) { ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)("File watching init failed: " + ex.Message)); } } } private void CreateFileIfNotExists(string filePath, bool isAlly) { try { if (!File.Exists(filePath)) { string directoryName = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } string contents = "# H3TVR " + (isAlly ? "Ally" : "Enemy") + " Chat File (Channel Points Compatible)\n# SUPPORTED FORMATS:\n# Plain username: ViewerName\n# Key=Value: username=ViewerName\n# Key=Value: user=ViewerName\n# Key=Value: redeemer=ViewerName\n# File will be cleared after reading if ClearFileAfterRead is enabled\n# Perfect for Twitch Channel Points redemptions!\n"; File.WriteAllText(filePath, contents); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Created chat file: " + filePath)); } } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Failed to create file " + filePath + ": " + ex.Message)); } } } private void Update() { try { HandleManualInput(); if (enableFileWatching.Value && Time.time - lastFileCheckTime >= fileCheckInterval.Value) { CheckChatFiles(); lastFileCheckTime = Time.time; } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Update loop error: " + ex.Message)); } } } private void HandleManualInput() { //IL_0007: 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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(manualAllySpawnKey.Value)) { SpawnManualAlly(); } if (Input.GetKeyDown(manualEnemySpawnKey.Value)) { SpawnManualEnemy(); } if (Input.GetKeyDown(clearAllSosigsKey.Value)) { ClearAllSosigs(); } } private void SpawnManualAlly() { try { string text = "Player_" + Random.Range(1000, 9999); sosigSpawner?.SpawningSequence(text); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Manually spawned ally: " + text)); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Manual ally spawn failed: " + ex.Message)); } } } private void SpawnManualEnemy() { try { string text = "Enemy_" + Random.Range(1000, 9999); sosigSpawner?.SpawningSequenceEnemy(1, text); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Manually spawned enemy: " + text)); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Manual enemy spawn failed: " + ex.Message)); } } } private void ClearAllSosigs() { try { sosigSpawner?.ClearSosigs(); processedUsernames.Clear(); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Cleared all chat sosigs"); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Clear sosigs failed: " + ex.Message)); } } } private void CheckChatFiles() { string text = ResolveFilePath(allyChatFilePath.Value); if (File.Exists(text)) { ProcessChatFile(text, isAlly: true); } string text2 = ResolveFilePath(enemyChatFilePath.Value); if (File.Exists(text2)) { ProcessChatFile(text2, isAlly: false); } } private void ProcessChatFile(string filePath, bool isAlly) { try { string text = File.ReadAllText(filePath); string text2 = (isAlly ? lastAllyFileContent : lastEnemyFileContent); if (text == text2) { return; } if (isAlly) { lastAllyFileContent = text; } else { lastEnemyFileContent = text; } if (string.IsNullOrEmpty(text) || text.Trim().Length == 0) { return; } ParsedContentResult parsedContentResult = ParseContent(text); if (parsedContentResult.ArmorCommands.Count > 0) { foreach (KeyValuePair<string, int> armorCommand in parsedContentResult.ArmorCommands) { if (armorCommand.Key == "ally_armor") { SosigCustomizationUI.AllyArmor.Value = armorCommand.Value; SosigArmorManager.SetGlobalDefaults(armorCommand.Value, SosigCustomizationUI.EnemyArmor.Value); ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)("Set ally armor to " + SosigArmorManager.GetArmorName(armorCommand.Value))); } } else if (armorCommand.Key == "enemy_armor") { SosigCustomizationUI.EnemyArmor.Value = armorCommand.Value; SosigArmorManager.SetGlobalDefaults(SosigCustomizationUI.AllyArmor.Value, armorCommand.Value); ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)("Set enemy armor to " + SosigArmorManager.GetArmorName(armorCommand.Value))); } } } } foreach (UserArmorCommand userArmorCommand in parsedContentResult.UserArmorCommands) { if (!string.IsNullOrEmpty(userArmorCommand.Username)) { SosigArmorManager.SetUserArmorPreference(userArmorCommand.Username, userArmorCommand.ArmorLevel); ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)("[ARMOR] " + userArmorCommand.Username + " set armor to " + userArmorCommand.ArmorName)); } continue; } SosigCustomizationUI.SetAllyArmor(userArmorCommand.ArmorLevel); SosigCustomizationUI.SetEnemyArmor(userArmorCommand.ArmorLevel); SosigArmorManager.SetGlobalDefaults(userArmorCommand.ArmorLevel, userArmorCommand.ArmorLevel); ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogInfo((object)("[ARMOR] Global armor set to " + userArmorCommand.ArmorName)); } } foreach (string username in parsedContentResult.Usernames) { if (processedUsernames.Contains(username)) { continue; } if (isAlly) { sosigSpawner?.SpawningSequence(username); ManualLogSource obj5 = logger; if (obj5 != null) { obj5.LogInfo((object)("Channel Point: Spawned ally for " + username + " (Armor: " + SosigArmorManager.GetArmorName(SosigArmorManager.GetUserArmorPreference(username, isAlly: true)) + ")")); } } else { sosigSpawner?.SpawningSequenceEnemy(1, username); ManualLogSource obj6 = logger; if (obj6 != null) { obj6.LogInfo((object)("Channel Point: Spawned enemy for " + username + " (Armor: " + SosigArmorManager.GetArmorName(SosigArmorManager.GetUserArmorPreference(username, isAlly: false)) + ")")); } } processedUsernames.Add(username); } if (clearFileAfterRead.Value && (parsedContentResult.Usernames.Count > 0 || parsedContentResult.ArmorCommands.Count > 0)) { ClearChatFile(filePath, isAlly); } if (processedUsernames.Count > 1000) { processedUsernames.Clear(); } } catch (Exception ex) { ManualLogSource obj7 = logger; if (obj7 != null) { obj7.LogError((object)("Failed to process chat file " + filePath + ": " + ex.Message)); } } } private ParsedContentResult ParseContent(string content) { ParsedContentResult parsedContentResult = default(ParsedContentResult); parsedContentResult.Usernames = new List<string>(); parsedContentResult.ArmorCommands = new List<KeyValuePair<string, int>>(); parsedContentResult.UserArmorCommands = new List<UserArmorCommand>(); ParsedContentResult result = parsedContentResult; try { string[] array = content.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (string.IsNullOrEmpty(text2) || text2.StartsWith("#") || text2.StartsWith(";")) { continue; } if (TryParseUserArmorCommand(text2, out var cmd)) { result.UserArmorCommands.Add(cmd); ManualLogSource obj = logger; if (obj != null) { obj.LogDebug((object)("User armor command: " + cmd.Username + " -> " + cmd.ArmorName)); } continue; } if (TryParseArmorCommand(text2, out KeyValuePair<string, int> command)) { result.ArmorCommands.Add(command); ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogDebug((object)$"Parsed armor command: '{command.Key}={command.Value}'"); } continue; } if (TryParseAirdropCommand(text2, out string username)) { plugin.GetAirdropManager()?.CallAirdrop(username); continue; } string text3 = ExtractUsername(text2); if (!string.IsNullOrEmpty(text3)) { result.Usernames.Add(text3); ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogDebug((object)("Extracted username: '" + text3 + "' from line: '" + text2 + "'")); } } } } catch (Exception ex) { ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)("Failed to parse content: " + ex.Message)); } } return result; } private bool TryParseUserArmorCommand(string line, out UserArmorCommand cmd) { cmd = default(UserArmorCommand); string text = line.ToLower(); if (text.Contains(":!armor")) { int num = line.IndexOf(':'); if (num > 0) { string username = line.Substring(0, num).Trim(); string message = line.Substring(num + 1).Trim(); if (SosigArmorManager.TryParseArmorCommand(message, username, out int armorLevel, out string armorName)) { cmd = new UserArmorCommand { Username = username, ArmorLevel = armorLevel, ArmorName = armorName }; return true; } } } if (text.StartsWith("!armor") && SosigArmorManager.TryParseArmorCommand(line, null, out int armorLevel2, out string armorName2)) { cmd = new UserArmorCommand { Username = null, ArmorLevel = armorLevel2, ArmorName = armorName2 }; return true; } if (text.Contains("armor=") && (text.Contains("user=") || text.Contains("username="))) { string text2 = null; int level = 0; string[] array = line.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text3 in array2) { if (text3.ToLower().StartsWith("user=") || text3.ToLower().StartsWith("username=")) { text2 = text3.Substring(text3.IndexOf('=') + 1).Trim(); } else if (text3.ToLower().StartsWith("armor=")) { string value = text3.Substring(6).Trim(); ParseArmorValue(value, out level); } } if (!string.IsNullOrEmpty(text2)) { SosigArmorManager.SetUserArmorPreference(text2, level); cmd = new UserArmorCommand { Username = text2, ArmorLevel = level, ArmorName = SosigArmorManager.GetArmorName(level) }; return true; } } return false; } private bool ParseArmorValue(string value, out int level) { level = 0; if (string.IsNullOrEmpty(value)) { return false; } if (int.TryParse(value, out level)) { level = Mathf.Clamp(level, 0, 5); return true; } switch (value.ToLower()) { case "none": case "off": level = 0; return true; case "light": case "l": level = 1; return true; case "medium": case "med": case "m": level = 2; return true; case "heavy": case "h": level = 3; return true; case "tank": case "juggernaut": case "jug": case "t": level = 4; return true; case "god": case "godmode": case "g": level = 5; return true; default: return false; } } private bool TryParseArmorCommand(string line, out KeyValuePair<string, int> command) { command = default(KeyValuePair<string, int>); if (!line.Contains("=")) { return false; } string[] array = line.Split(new char[1] { '=' }); if (array.Length < 2) { return false; } string text = array[0].Trim().ToLower(); if ((text == "ally_armor" || text == "enemy_armor") && int.TryParse(array[1].Trim(), out var result)) { command = new KeyValuePair<string, int>(text, Mathf.Clamp(result, 0, 5)); return true; } return false; } private bool TryParseAirdropCommand(string line, out string username) { username = null; if (!line.Contains("=")) { return false; } string[] array = line.Split(new char[1] { '=' }); if (array.Length < 2) { return false; } string text = array[0].Trim().ToLower(); if (text == "airdrop") { username = array[1].Trim(); return true; } return false; } private List<string> ParseUsernames(string content) { return ParseContent(content).Usernames; } private string ExtractUsername(string line) { if (string.IsNullOrEmpty(line)) { return null; } string text = line.Trim(); if (text.Contains("=")) { string[] array = text.Split(new char[1] { '=' }); if (array.Length >= 2) { string text2 = array[0].Trim().ToLower(); string text3 = array[1].Trim(); switch (text2) { default: if (!(text2 == "name")) { break; } goto case "username"; case "username": case "user": case "redeemer": { int num = text3.IndexOf('#'); if (num > 0) { text3 = text3.Substring(0, num).Trim(); } int num2 = text3.IndexOf(' '); if (num2 > 0) { text3 = text3.Substring(0, num2).Trim(); } if (!string.IsNullOrEmpty(text3)) { return text3; } break; } } } } if (text.Contains(" ")) { string[] array2 = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length != 0) { string text4 = array2[0].Trim(); if (!text4.StartsWith("[") && !text4.StartsWith("<") && !text4.EndsWith(":") && text4.Length > 0) { return text4; } } } if (text.Length > 0 && !text.StartsWith("[") && !text.StartsWith("<")) { return text; } return null; } private void ClearChatFile(string filePath, bool isAlly) { try { string contents = "# H3TVR " + (isAlly ? "Ally" : "Enemy") + " Chat File (Channel Points Compatible)\n# SUPPORTED FORMATS:\n# Plain username: ViewerName\n# Key=Value: username=ViewerName\n# Key=Value: user=ViewerName\n# Key=Value: redeemer=ViewerName\n"; File.WriteAllText(filePath, contents); if (isAlly) { lastAllyFileContent = ""; } else { lastEnemyFileContent = ""; } } catch (Exception ex) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)("Failed to clear file " + filePath + ": " + ex.Message)); } } } public void TriggerSpawn(string username, bool isAlly) { try { if (string.IsNullOrEmpty(username)) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)"Cannot spawn sosig - username is null or empty"); } return; } if (isAlly) { sosigSpawner?.SpawningSequence(username); ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogInfo((object)("API trigger: Spawned ally for " + username)); } } else { sosigSpawner?.SpawningSequenceEnemy(1, username); ManualLogSource obj3 = logger; if (obj3 != null) { obj3.LogInfo((object)("API trigger: Spawned enemy for " + username)); } } processedUsernames.Add(username); } catch (Exception ex) { ManualLogSource obj4 = logger; if (obj4 != null) { obj4.LogError((object)("API spawn trigger failed: " + ex.Message)); } } } public ChatWatcherStats GetStats() { AdvancedChatSosigSpawner.SosigStats sosigStats = sosigSpawner?.GetStats() ?? default(AdvancedChatSosigSpawner.SosigStats); ChatWatcherStats result = default(ChatWatcherStats); result.FileWatchingActive = enableFileWatching.Value; result.ProcessedUsernames = processedUsernames.Count; result.ActiveAllies = sosigStats.Allies; result.ActiveEnemies = sosigStats.Enemies; result.TotalActiveSosigs = sosigStats.TotalActive; return result; } public void ClearCache() { processedUsernames.Clear(); lastAllyFileContent = ""; lastEnemyFileContent = ""; ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Cleared chat watcher cache"); } } private string ResolveFilePath(string configuredPath) { if (string.IsNullOrEmpty(configuredPath)) { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)plugin).Info.Location); string path = Path.Combine(directoryName, ".."); string path2 = Path.Combine(path, "config"); return Path.Combine(path2, "H3TVR_Chat.txt"); } if (Path.IsPathRooted(configuredPath)) { return configuredPath; } string directoryName2 = Path.GetDirectoryName(((BaseUnityPlugin)plugin).Info.Location); string text = Path.Combine(directoryName2, configuredPath); if (File.Exists(text)) { return text; } string directoryName3 = Path.GetDirectoryName(directoryName2); string text2 = Path.Combine(directoryName3, configuredPath); if (File.Exists(text2)) { return text2; } return text; } private void OnDestroy() { try { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)"Chat Watcher cleaned up"); } } catch (Exception ex) { ManualLogSource obj2 = logger; if (obj2 != null) { obj2.LogError((object)("Chat Watcher cleanup failed: " + ex.Message)); } } } } [BepInPlugin("com.MrBeam.h3tvr", "H3TVR", "1.1.4")] [BepInProcess("h3vr.exe")] public class H3TVRImproved : BaseUnityPlugin { private enum EncryptionType { Unknown, Pattern, Sequence, Timed } [CompilerGenerated] private sealed class <AutoCompleteEncryptionDelayed>d__107 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public TNH_HoldPointSystemNode encryptionNode; public float delay; public H3TVRImproved <>4__this; private Exception <ex>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AutoCompleteEncryptionDelayed>d__107(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <ex>5__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(delay); <>1__state = 1; return true; case 1: <>1__state = -1; try { if ((Object)(object)encryptionNode != (Object)null && (Object)(object)((Component)encryptionNode).gameObject != (Object)null && ((Component)encryptionNode).gameObject.activeSelf) { <>4__this.CompleteEncryptionNode(encryptionNode); ((BaseUnityPlugin)<>4__this).Logger.LogDebug((object)$"[TNH] Auto-completed encryption after {delay}s delay"); } } catch (Exception ex) { <ex>5__1 = ex; ((BaseUnityPlugin)<>4__this).Logger.LogDebug((object)("[TNH] Error auto-completing encryption: " + <ex>5__1.Message)); } 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(); } } [CompilerGenerated] private sealed class <DelayedArmorSystemInitialization>d__95 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public H3TVRImproved <>4__this; private MethodInfo <method>5__1; private Exception <ex>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedArmorSystemInitialization>d__95(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <method>5__1 = null; <ex>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Delayed armor system initialization completed"); <>2__current = (object)new WaitForSeconds(2f); <>1__state = 2; return true; case 2: <>1__state = -1; if ((Object)(object)<>4__this.advancedChatSpawner != (Object)null) { ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Retrying template cache build after H3VR initialization..."); <method>5__1 = ((object)<>4__this.advancedChatSpawner).GetType().GetMethod("BuildTemplateCache", BindingFlags.Instance | BindingFlags.NonPublic); if ((object)<method>5__1 != null) { try { <method>5__1.Invoke(<>4__this.advancedChatSpawner, null); ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Template cache rebuild completed"); } catch (Exception ex) { <ex>5__2 = ex; ((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)("Template cache rebuild warning: " + <ex>5__2.Message)); } } <method>5__1 = 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 const float SlowdownFactor = 0.001f; private const float SlowdownLength = 6f; private const float ZeroGWaitTime = 6f; private const float RealisticFallTime = 1f; private const float MalfunctionBoostDuration = 120f; private const float ForcedMalfunctionChance = 0.75f; private string slomoStatus = "Off"; private string zeroGStatus = "Off"; private bool malfunctionBoostActive; private float malfunctionBoostEndTime; private float slomoRampStartTime; private float slomoRampStartValue; private bool isRamping; private ConfigEntry<float> maxSlomo; private ConfigEntry<float> slomoWaitTime; private ConfigEntry<float> slomoScaleSpeed; private ConfigEntry<float> slomoReturnSpeed; private ConfigEntry<bool> slomoVRControllerEnabled; private ConfigEntry<string> slomoVRButton; private ConfigEntry<bool> slomoAffectsMovement; private ConfigEntry<float> slomoMovementScale; private ConfigEntry<bool> slomoUseRampSpeed; private ConfigEntry<string> slomoRampCurve; private ConfigEntry<float> slomoRampDuration; private ConfigEntry<float> slomoReturnRampDuration; private ConfigEntry<bool> enableKillSlomo; private ConfigEntry<bool> slomoAffectsAudio; private ConfigEntry<float> slomoAudioPitchScale; private ConfigEntry<bool> slomoAudioPreservePitch; private ConfigEntry<bool> slomoAffectsAudioSpeed; private ConfigEntry<float> slomoAudioSpeedScale; private ConfigEntry<string> slomoAudioMode; private ConfigEntry<bool> useItemManagerForGunRandomization; private ConfigEntry<string> gunList; private ConfigEntry<string> magazineList; private ConfigEntry<float> shurikenScale; private ConfigEntry<int> shurikenMinCount; private ConfigEntry<int> shurikenMaxCount; private ConfigEntry<int> pillowMinCount; private ConfigEntry<int> pillowMaxCount; private ConfigEntry<bool> pillowGrenadeEnabled; private ConfigEntry<float> pillowGrenadeChance; private ConfigEntry<float> pillowGrenadeArmedChance; private ConfigEntry<bool> pillowZeroGravityEnabled; private ConfigEntry<float> pillowZeroGravityChance; private ConfigEntry<float> pillowZeroGravityDuration; private ConfigEntry<bool> pillowSlomoEnabled; private ConfigEntry<float> pillowSlomoChance; private ConfigEntry<float> pillowSlomoDuration; private ConfigEntry<int> dangerCloseMinCount; private ConfigEntry<int> dangerCloseMaxCount; private readonly Dictionary<string, ConfigEntry<KeyCode>> keyBindings = new Dictionary<string, ConfigEntry<KeyCode>>(); private ConfigEntry<bool> enableTwitchChatSosigs; private ConfigEntry<bool> enableLegacyFileMode; private ConfigEntry<string> twitchChatFilePath; private ConfigEntry<string> twitchEnemyChatFilePath; private ConfigEntry<int> maxChatSosigs; private ConfigEntry<bool> enableSteamFriends; private ConfigEntry<bool> steamFriendsRandomNames; private ConfigEntry<float> steamFriendsRefreshInterval; private ConfigEntry<bool> enableInfiniteTokens; private ConfigEntry<bool> disableEncryptionNodes; private ConfigEntry<bool> disableAllEncryptions; private ConfigEntry<bool> disableEncryptionType1; private ConfigEntry<bool> disableEncryptionType2; private ConfigEntry<bool> disableEncryptionType3; private ConfigEntry<bool> autoCompleteEncryption; private ConfigEntry<float> encryptionCompletionDelay; private SlomoMovementController slomoMovementController; private readonly Hooks hooks = new Hooks(); private InputHandler inputHandler; private SpawnManager spawnManager; private EffectsManager effectsManager; private WeaponManager weaponManager; private AudioManager audioManager; private AdvancedChatSosigSpawner advancedChatSpawner; private SosigArmorWristMenuIntegration sosigArmorWristMenu; private SteamFriendsIntegration steamFriendsIntegration; private SosigCustomizationUI sosigCustomizationUI; private AirdropManager airdropManager; private LioranBoardIntegration lioranBoardIntegration; private static Dictionary<AudioSource, float> originalAudioSpeeds = new Dictionary<AudioSource, float>(); public H3TVRImproved() { hooks.Hook(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loading H3TVR Enhanced Edition (Standalone Mode)"); } private void Awake() { try { OptionalDependencyManager.Initialize(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"H3TVR Enhanced Edition (Standalone Mode) is loading..."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 1: Initializing configuration..."); InitializeConfiguration(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 2: Initializing optional dependencies..."); InitializeOptionalDependencies(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 3: Initializing components..."); InitializeComponents(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 3.5: Initializing UI..."); InitializeUI(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 3.6: Initializing Airdrop Manager..."); InitializeAirdropManager(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 4: Initializing Sosig Spawner..."); InitializeSosigSpawner(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 5: Initializing SpawnManager..."); if ((Object)(object)spawnManager != (Object)null && (Object)(object)advancedChatSpawner != (Object)null) { spawnManager.Initialize(this, ((BaseUnityPlugin)this).Logger, advancedChatSpawner, audioManager); ((BaseUnityPlugin)this).Logger.LogInfo((object)"SpawnManager initialized successfully"); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Cannot initialize SpawnManager - spawnManager: {(Object)(object)spawnManager != (Object)null}, advancedChatSpawner: {(Object)(object)advancedChatSpawner != (Object)null}"); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 6: Initializing Twitch integration..."); InitializeTwitchIntegration(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 6.5: Initializing Steam Friends integration..."); InitializeSteamFriendsIntegration(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 6.6: Initializing LioranBoard 2 integration..."); InitializeLioranBoardIntegration(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Step 7: Initializing wrist menu..."); try { InitializeSosigArmorWristMenuIntegration(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Non-critical error in wrist menu integration: " + ex.Message)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"H3TVR Enhanced Edition loaded successfully!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)OptionalDependencyManager.GetDependencyStatusReport()); if (MeatyceiverIntegrationManager.IsIntegrationEnabled()) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Meatyceiver 2 Integration: ACTIVE"); ((BaseUnityPlugin)this).Logger.LogInfo((object)MeatyceiverIntegrationManager.GetTransformationStats()); } if (enableTwitchChatSosigs != null && enableTwitchChatSosigs.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Chat Sosig System: ENABLED"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" - Standalone mode (no Twitch integration)"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" - Use keyboard: P (ally), O (enemy), Delete (clear)"); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Chat Sosig System: DISABLED"); } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error during H3TVR initialization: " + ex2.Message)); ((BaseUnityPlugin)this).Logger.LogError((object)("Stack trace: " + ex2.StackTrace)); try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Attempting fallback initialization..."); InitializeConfiguration(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"H3TVR running in fallback mode with basic functionality"); } catch (Exception ex3) { ((BaseUnityPlugin)this).Logger.LogError((object)("Critical error - H3TVR cannot initialize: " + ex3.Message)); } } } private void InitializeConfiguration() { maxSlomo = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo", "MaxSlowmoScale", 0.1f, "Maximum slomo scale (0.01 = 1% speed, 0.1 = 10% speed)"); slomoWaitTime = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo", "WaitTime", 2f, "Time to wait at max slomo before returning to normal speed"); slomoScaleSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo", "ScaleDownSpeed", 1f, "Speed at which time slows down (higher = faster transition)"); slomoReturnSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo", "ReturnSpeed", 0.33f, "Speed at which time returns to normal (higher = faster return)"); slomoVRControllerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Slomo", "VRControllerEnabled", true, "Enable VR controller button to trigger slomo"); slomoVRButton = ((BaseUnityPlugin)this).Config.Bind<string>("Slomo", "VRButton", "LeftX", "VR button to trigger slomo"); slomoAffectsMovement = ((BaseUnityPlugin)this).Config.Bind<bool>("Slomo", "AffectsMovement", true, "Whether slomo affects player movement speed"); slomoMovementScale = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo", "MovementScale", 0.3f, "Movement speed multiplier during slomo"); slomoUseRampSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("Slomo.Ramp", "UseRampSpeed", true, "Enable smooth ramp speed transitions for slomo (more cinematic)"); slomoRampCurve = ((BaseUnityPlugin)this).Config.Bind<string>("Slomo.Ramp", "RampCurve", "EaseInOut", "Curve type for slomo ramp: Linear, EaseIn, EaseOut, EaseInOut, Smooth, Cinematic"); slomoRampDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo.Ramp", "RampDuration", 0.5f, "Duration in seconds for slomo to ramp down to max slow speed"); slomoReturnRampDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Slomo.Ramp", "ReturnRampDuration", 0.8f, "Duration in seconds for slomo to ramp back to normal speed"); enableKillSlomo = ((BaseUnityPlugin)this).Config.Bind<bool>("Slomo", "EnableKillSlomo", true, "Enable slow motion effect on enemy kill."); slomoAffectsAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "SlomoAffectsAudio", true, "Whether slomo affects audio pitch"); slomoAudioPitchScale = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "SlomoAudioPitchScale", 1f, "Audio pitch multiplier during slomo (1.0 = normal pitch, 0.5 = half pitch)"); slomoAudioPreservePitch = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "SlomoPreservePitch", false, "If true, audio pitch is preserved (no pitch change). If false, uses pitch scaling."); slomoAffectsAudioSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "SlomoAffectsAudioSpeed", false, "Whether slomo affects audio speed (time stretching)"); slomoAudioSpeedScale = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "SlomoAudioSpeedScale", 1f, "Audio speed multiplier during slomo (1.0 = normal speed, 0.5 = half speed)"); slomoAudioMode = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "SlomoAudioMode", "Both", "Audio adjustment mode during slomo: 'PitchOnly', 'SpeedOnly', 'Both', 'Independent'"); useItemManagerForGunRandomization = ((BaseUnityPlugin)this).Config.Bind<bool>("GunRandomization", "UseItemManager", true, "Use ItemManager for gun randomization (includes all H3VR and modded guns). If false, uses GunList/MagazineList config files."); gunList = ((BaseUnityPlugin)this).Config.Bind<string>("General", "GunList", "DefaultGunList", "List of guns"); magazineList = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MagazineList", "DefaultMagazineList", "List of magazines"); InitializeSpawnConfigurations(); InitializeKeyBindings(); } private void InitializeSpawnConfigurations() { shurikenScale = ((BaseUnityPlugin)this).Config.Bind<float>("Shuriken", "Scale", 1f, "Scale multiplier for spawned shuriken"); shurikenMinCount = ((BaseUnityPlugin)this).Config.Bind<int>("Shuriken", "MinCount", 1, "Minimum number of shuriken to spawn"); shurikenMaxCount = ((BaseUnityPlugin)this).Config.Bind<int>("Shuriken", "MaxCount", 3, "Maximum number of shuriken to spawn"); pillowMinCount = ((BaseUnityPlugin)this).Config.Bind<int>("Pillow", "MinCount", 1, "Minimum number of pillows to spawn"); pillowMaxCount = ((BaseUnityPlugin)this).Config.Bind<int>("Pillow", "MaxCount", 3, "Maximum number of pillows to spawn"); pillowGrenadeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Pillow", "GrenadeEnabled", true, "Enable pillow grenade effect"); pillowGrenadeChance = ((BaseUnityPlugin)this).Config.Bind<float>("Pillow", "GrenadeChance", 0.1f, "Chance for pillow to spawn grenade"); pillowGrenadeArmedChance = ((BaseUnityPlugin)this).Config.Bind<float>("Pillow", "GrenadeArmedChance", 0.3f, "Chance for pillow grenade to be armed"); pillowZeroGravityEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Pillow", "ZeroGEnabled", true, "Enable pillow zero gravity effect"); pillowZeroGravityChance = ((BaseUnityPlugin)this).Config.Bind<float>("Pillow", "ZeroGChance", 0.15f, "Chance for pillow to trigger zero gravity"); pillowZeroGravityDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Pillow", "ZeroGDuration", 10f, "Duration of pillow zero gravity effect"); pillowSlomoEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Pillow", "SlomoEnabled", true, "Enable pillow slow motion effect"); pillowSlomoChance =