using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("WearableTorches")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WearableTorches")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a614924a-5167-4a9b-96b5-859ddb3331ad")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace WearableTorches;
[BepInPlugin("com.thomas.wearabletorches", "Wearable Torches", "1.0.6")]
public class WearableTorchesPlugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
private Harmony _harmony;
private void Awake()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
_harmony = new Harmony("com.thomas.wearabletorches");
_harmony.PatchAll();
Log.LogInfo((object)"Wearable Torches loaded (G key, torch required in hand)");
}
private void Update()
{
if (ZInput.instance == null)
{
return;
}
Player localPlayer = Player.m_localPlayer;
if ((Object)(object)localPlayer != (Object)null)
{
WearableTorchManager.HandleLocalInput(localPlayer);
}
foreach (Player allPlayer in Player.GetAllPlayers())
{
WearableTorchManager.SyncBackTorchForPlayer(allPlayer);
}
}
private void OnDestroy()
{
WearableTorchManager.ForceDestroyBackTorch();
}
}
public static class WearableTorchManager
{
private static readonly Dictionary<long, GameObject> BackTorches = new Dictionary<long, GameObject>();
public const string ZdoKeyBackTorch = "WearableTorches_BackTorch";
public const string ZdoKeyBackTorchPrefab = "WearableTorches_BackTorchPrefab";
private static readonly Vector3 FlameOffset = new Vector3(0f, 0f, 0.61f);
private static readonly Vector3 LightOffset = new Vector3(0f, 0f, 0.61f);
private static readonly Quaternion BackRotationOffset = Quaternion.Euler(-80f, 0f, 0f);
private static long GetPlayerKey(Player player)
{
if ((Object)(object)player == (Object)null)
{
return 0L;
}
return player.GetPlayerID();
}
private static GameObject FindExistingBackTorchObject(Player player)
{
if ((Object)(object)player == (Object)null)
{
return null;
}
Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren<Transform>(true);
foreach (Transform val in componentsInChildren)
{
if (((Object)val).name == "BackTorchObject")
{
return ((Component)val).gameObject;
}
}
return null;
}
public static void HandleLocalInput(Player player)
{
ZNetView nView = GetNView(player);
if ((Object)(object)nView == (Object)null || !nView.IsOwner() || !ZInput.GetKeyDown((KeyCode)103, true))
{
return;
}
ZDO zDO = nView.GetZDO();
if (zDO == null)
{
return;
}
if (!zDO.GetBool("WearableTorches_BackTorch", false))
{
ItemData torchInHands = GetTorchInHands(player);
if (torchInHands == null)
{
WearableTorchesPlugin.Log.LogInfo((object)("Back torch: no torch in hand, toggle ignored for " + player.GetPlayerName()));
return;
}
GameObject dropPrefab = torchInHands.m_dropPrefab;
if ((Object)(object)dropPrefab == (Object)null)
{
WearableTorchesPlugin.Log.LogWarning((object)("Back torch: torch in hand has no dropPrefab for " + player.GetPlayerName()));
return;
}
string name = ((Object)dropPrefab).name;
if (!string.IsNullOrEmpty(name))
{
zDO.Set("WearableTorches_BackTorchPrefab", name);
zDO.Set("WearableTorches_BackTorch", true);
HideVanillaTorchVisuals(player, hide: true);
WearableTorchesPlugin.Log.LogInfo((object)("Back torch ON (G key, from hand) for " + player.GetPlayerName() + " prefab=" + name));
}
}
else
{
zDO.Set("WearableTorches_BackTorch", false);
zDO.Set("WearableTorches_BackTorchPrefab", "");
HideVanillaTorchVisuals(player, hide: false);
WearableTorchesPlugin.Log.LogInfo((object)("Back torch OFF (G key) for " + player.GetPlayerName()));
}
}
public static void SyncBackTorchForPlayer(Player player)
{
if ((Object)(object)player == (Object)null)
{
return;
}
ZNetView nView = GetNView(player);
if ((Object)(object)nView == (Object)null)
{
return;
}
ZDO zDO = nView.GetZDO();
if (zDO == null)
{
return;
}
bool @bool = zDO.GetBool("WearableTorches_BackTorch", false);
string @string = zDO.GetString("WearableTorches_BackTorchPrefab", "");
long playerKey = GetPlayerKey(player);
if (playerKey == 0L)
{
return;
}
BackTorches.TryGetValue(playerKey, out var value);
if (@bool && !string.IsNullOrEmpty(@string))
{
if ((Object)(object)value != (Object)null)
{
return;
}
GameObject val = FindExistingBackTorchObject(player);
if ((Object)(object)val != (Object)null)
{
BackTorches[playerKey] = val;
return;
}
GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(@string) : null);
if ((Object)(object)val2 == (Object)null)
{
WearableTorchesPlugin.Log.LogWarning((object)("Back torch: prefab '" + @string + "' not found in ZNetScene"));
return;
}
GameObject val3 = CreateBackTorchVisual(player, val2);
if ((Object)(object)val3 != (Object)null)
{
BackTorches[playerKey] = val3;
}
}
else if ((Object)(object)value != (Object)null)
{
Object.Destroy((Object)(object)value);
BackTorches.Remove(playerKey);
}
else
{
GameObject val4 = FindExistingBackTorchObject(player);
if ((Object)(object)val4 != (Object)null)
{
Object.Destroy((Object)(object)val4);
}
}
}
private static ZNetView GetNView(Player player)
{
if (!((Object)(object)player != (Object)null))
{
return null;
}
return ((Component)player).GetComponent<ZNetView>();
}
internal static bool IsTorch(ItemData item)
{
if (item == null)
{
return false;
}
return ((item.m_shared != null) ? item.m_shared.m_name : "").ToLower().Contains("torch");
}
private static ItemData GetTorchInHands(Player player)
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
try
{
MethodInfo method = typeof(Player).GetMethod("GetRightItem", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo method2 = typeof(Player).GetMethod("GetLeftItem", BindingFlags.Instance | BindingFlags.NonPublic);
ItemData val = ((!(method != null)) ? ((ItemData)null) : ((ItemData)method.Invoke(player, null)));
ItemData val2 = ((!(method2 != null)) ? ((ItemData)null) : ((ItemData)method2.Invoke(player, null)));
if (IsTorch(val))
{
return val;
}
if (IsTorch(val2))
{
return val2;
}
return null;
}
catch (Exception ex)
{
WearableTorchesPlugin.Log.LogError((object)("GetTorchInHands reflection error: " + ex));
return null;
}
}
private static GameObject CreateBackTorchVisual(Player player, GameObject prefab)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: 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_0071: 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_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: 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_00b8: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: 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_0196: 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_01a6: Unknown result type (might be due to invalid IL or missing references)
//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
//IL_0161: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)prefab == (Object)null)
{
return null;
}
GameObject val = FindExistingBackTorchObject(player);
if ((Object)(object)val != (Object)null)
{
return val;
}
Transform val2 = FindBackAttach(player);
GameObject val3 = new GameObject("BackTorchObject");
val3.transform.SetParent(val2, false);
val3.transform.localRotation = BackRotationOffset;
val3.transform.localPosition = new Vector3(-0.0026f, -0.002f, -0.0017f);
Vector3 lossyScale = val2.lossyScale;
val3.transform.localScale = new Vector3((lossyScale.x != 0f) ? (1f / lossyScale.x) : 1f, (lossyScale.y != 0f) ? (1f / lossyScale.y) : 1f, (lossyScale.z != 0f) ? (1f / lossyScale.z) : 1f);
MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<MeshRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
((Object)Object.Instantiate<GameObject>(((Component)componentsInChildren[i]).gameObject, val3.transform, false)).name = "BackTorchMesh";
}
GameObject val4 = FindTorchFlamesOnPlayer(player);
if ((Object)(object)val4 == (Object)null)
{
val4 = FindTorchFlamesInPrefab(prefab);
}
if ((Object)(object)val4 != (Object)null)
{
GameObject obj = Object.Instantiate<GameObject>(val4, val3.transform, false);
((Object)obj).name = "BackTorchFlames";
obj.transform.localRotation = Quaternion.identity;
obj.transform.localPosition = FlameOffset;
}
GameObject val5 = new GameObject("BackTorchLight");
val5.transform.SetParent(val3.transform, false);
val5.transform.localRotation = Quaternion.identity;
val5.transform.localPosition = LightOffset;
Light obj2 = val5.AddComponent<Light>();
obj2.type = (LightType)2;
obj2.color = new Color(1f, 0.52f, 0.22f);
obj2.range = 14f;
obj2.intensity = 1.35f;
obj2.shadows = (LightShadows)2;
return val3;
}
internal static void HideVanillaTorchVisuals(Player player, bool hide)
{
if ((Object)(object)player == (Object)null)
{
return;
}
MeshRenderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<MeshRenderer>(true);
foreach (MeshRenderer val in componentsInChildren)
{
Transform transform = ((Component)val).transform;
if (!IsUnderBackTorchObject(transform))
{
string text = ((Object)transform).name.ToLower();
string text2 = (((Object)(object)transform.parent != (Object)null) ? ((Object)transform.parent).name.ToLower() : "");
if (text.Contains("torch") || text2.Contains("torch"))
{
((Renderer)val).enabled = !hide;
}
}
}
SkinnedMeshRenderer[] componentsInChildren2 = ((Component)player).GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (SkinnedMeshRenderer val2 in componentsInChildren2)
{
Transform transform2 = ((Component)val2).transform;
if (!IsUnderBackTorchObject(transform2))
{
string text3 = ((Object)transform2).name.ToLower();
string text4 = (((Object)(object)transform2.parent != (Object)null) ? ((Object)transform2.parent).name.ToLower() : "");
if (text3.Contains("torch") || text4.Contains("torch"))
{
((Renderer)val2).enabled = !hide;
}
}
}
}
private static bool IsUnderBackTorchObject(Transform t)
{
while ((Object)(object)t != (Object)null)
{
if (((Object)t).name == "BackTorchObject")
{
return true;
}
t = t.parent;
}
return false;
}
private static Transform FindBackAttach(Player player)
{
string[] array = new string[3] { "Visual/Armature/Hips/Spine/Spine1/Spine2", "Visual/Armature/Hips/Spine/Spine1", "Visual/Armature/Hips/Spine" };
foreach (string text in array)
{
Transform val = ((Component)player).transform.Find(text);
if ((Object)(object)val != (Object)null)
{
return val;
}
}
return ((Component)player).transform;
}
private static GameObject FindTorchFlamesOnPlayer(Player player)
{
Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren<Transform>(true);
foreach (Transform val in componentsInChildren)
{
if (((Object)val).name.ToLower().Contains("torch") && !((Object)(object)((Component)val).GetComponentInChildren<ParticleSystem>(true) == (Object)null) && !((Object)(object)((Component)val).GetComponentInChildren<MeshRenderer>(true) != (Object)null) && !((Object)(object)((Component)val).GetComponentInChildren<SkinnedMeshRenderer>(true) != (Object)null))
{
return ((Component)val).gameObject;
}
}
return null;
}
private static GameObject FindTorchFlamesInPrefab(GameObject prefab)
{
if ((Object)(object)prefab == (Object)null)
{
return null;
}
ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
Transform transform = ((Component)componentsInChildren[i]).transform;
MeshRenderer component = ((Component)transform).GetComponent<MeshRenderer>();
SkinnedMeshRenderer component2 = ((Component)transform).GetComponent<SkinnedMeshRenderer>();
if ((Object)(object)component == (Object)null && (Object)(object)component2 == (Object)null)
{
return ((Component)transform).gameObject;
}
}
return null;
}
private static void DestroyBackTorch()
{
foreach (KeyValuePair<long, GameObject> backTorch in BackTorches)
{
if ((Object)(object)backTorch.Value != (Object)null)
{
Object.Destroy((Object)(object)backTorch.Value);
}
}
BackTorches.Clear();
}
public static void ForceDestroyBackTorch()
{
DestroyBackTorch();
}
public static void RemoveBackTorch(Player player)
{
if ((Object)(object)player == (Object)null)
{
return;
}
long playerKey = GetPlayerKey(player);
if (playerKey != 0L && BackTorches.TryGetValue(playerKey, out var value))
{
if ((Object)(object)value != (Object)null)
{
Object.Destroy((Object)(object)value);
}
BackTorches.Remove(playerKey);
}
GameObject val = FindExistingBackTorchObject(player);
if ((Object)(object)val != (Object)null)
{
Object.Destroy((Object)(object)val);
}
}
}
[HarmonyPatch(typeof(Player), "OnDestroy")]
public static class Player_OnDestroy_Patch
{
private static void Prefix(Player __instance)
{
WearableTorchManager.RemoveBackTorch(__instance);
}
}