Decompiled source of HelpfullWards v0.6.0

HelpfullWards.dll

Decompiled a day ago
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.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("HelpfullWards")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+983531e3e25bab94cbf35f92f43bde49abfdf158")]
[assembly: AssemblyProduct("HelpfullWards")]
[assembly: AssemblyTitle("HelpfullWards")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HelpfullWards
{
	public static class DamagedPieceTracker
	{
		public static readonly HashSet<WearNTear> Damaged = new HashSet<WearNTear>();

		public static void SeedFromAllInstances()
		{
			foreach (WearNTear allInstance in WearNTear.GetAllInstances())
			{
				if ((Object)(object)allInstance != (Object)null && allInstance.GetHealthPercentage() < 0.999f)
				{
					Damaged.Add(allInstance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "RPC_HealthChanged")]
	internal static class WearNTearHealthChangedPatch
	{
		private static void Postfix(WearNTear __instance, float health)
		{
			if (health < __instance.m_health * 0.999f)
			{
				DamagedPieceTracker.Damaged.Add(__instance);
			}
			else
			{
				DamagedPieceTracker.Damaged.Remove(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
	internal static class WearNTearDestroyPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			DamagedPieceTracker.Damaged.Remove(__instance);
		}
	}
	public static class EffectFinder
	{
		private const string DefaultOutputDir = "/home/michel/Documents/http/public/hierarchies";

		public static void Dump(string keyword, string fileName)
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"[EffectFinder] ZNetScene not ready, abort.");
				return;
			}
			string text = (Path.IsPathRooted(fileName) ? fileName : Path.Combine("/home/michel/Documents/http/public/hierarchies", fileName));
			List<(GameObject, bool)> list = new List<(GameObject, bool)>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if (Matches(prefab, keyword))
				{
					list.Add((prefab, true));
				}
			}
			foreach (GameObject nonNetViewPrefab in ZNetScene.instance.m_nonNetViewPrefabs)
			{
				if (Matches(nonNetViewPrefab, keyword))
				{
					list.Add((nonNetViewPrefab, false));
				}
			}
			list = list.OrderBy<(GameObject, bool), string>(((GameObject prefab, bool networked) m) => ((Object)m.prefab).name).ToList();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"# Prefabs matching \"{keyword}\" — {list.Count} hit(s)");
			stringBuilder.AppendLine();
			foreach (var item2 in list)
			{
				GameObject item = item2.Item1;
				string value = (item2.Item2 ? "[Net]" : "[NonNet]");
				stringBuilder.Append(value).Append(' ').Append(((Object)item).name)
					.Append(' ');
				stringBuilder.AppendLine(SummarizeComponents(item));
				DumpChildren(stringBuilder, item.transform, 1);
				stringBuilder.AppendLine();
			}
			Directory.CreateDirectory(Path.GetDirectoryName(text));
			File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8);
			Plugin.Logger.LogInfo((object)$"[EffectFinder] Wrote {list.Count} prefab(s) to: {text}");
		}

		private static bool Matches(GameObject? prefab, string keyword)
		{
			if ((Object)(object)prefab != (Object)null)
			{
				return ((Object)prefab).name.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static void DumpChildren(StringBuilder sb, Transform parent, int depth)
		{
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				sb.Append(new string(' ', depth * 2));
				sb.Append("└ ");
				sb.Append(((Object)child).name);
				sb.Append(' ');
				sb.AppendLine(SummarizeComponents(((Component)child).gameObject));
				DumpChildren(sb, child, depth + 1);
			}
		}

		private static string SummarizeComponents(GameObject go)
		{
			List<string> list = new List<string>();
			AppendCount<ParticleSystem>(go, "ParticleSystem", list);
			AppendCount<Light>(go, "Light", list);
			AppendCount<AudioSource>(go, "AudioSource", list);
			AppendCount<ZSFX>(go, "ZSFX", list);
			AppendCount<TrailRenderer>(go, "TrailRenderer", list);
			AppendCount<LineRenderer>(go, "LineRenderer", list);
			AppendCount<MeshRenderer>(go, "MeshRenderer", list);
			AppendCount<SkinnedMeshRenderer>(go, "SkinnedMeshRenderer", list);
			if ((Object)(object)go.GetComponent<ZNetView>() != (Object)null)
			{
				list.Add("ZNetView");
			}
			if ((Object)(object)go.GetComponent<Aoe>() != (Object)null)
			{
				list.Add("Aoe");
			}
			if ((Object)(object)go.GetComponent<TimedDestruction>() != (Object)null)
			{
				list.Add("TimedDestruction");
			}
			StatusEffect component = go.GetComponent<StatusEffect>();
			if ((Object)(object)component != (Object)null)
			{
				list.Add("SE:" + ((object)component).GetType().Name);
			}
			if (list.Count != 0)
			{
				return "[" + string.Join(", ", list) + "]";
			}
			return "";
		}

		private static void AppendCount<T>(GameObject go, string label, List<string> parts) where T : Component
		{
			int num = go.GetComponents<T>().Length;
			if (num != 0)
			{
				parts.Add((num == 1) ? label : $"{label}×{num}");
			}
		}
	}
	public class ElementalWardBehavior : WardBehavior
	{
		public enum Element
		{
			Fire,
			Frost,
			Poison,
			Lightning,
			Spirit
		}

		public Element DamageElement;

		private static HashSet<Faction>? _excluded;

		protected override float Interval => WardConfig.ElementalTickInterval.Value;

		private static HashSet<Faction> Excluded => _excluded ?? (_excluded = WardConfig.GetExcludedFactions());

		protected override bool Tick()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			float damage = GetDamage();
			List<Character> list = new List<Character>();
			Character.GetCharactersInRange(((Component)this).transform.position, Radius, list);
			List<Character> list2 = new List<Character>();
			foreach (Character item in list)
			{
				if (!((Object)(object)item == (Object)null) && !Excluded.Contains(item.m_faction) && !item.IsTamed())
				{
					list2.Add(item);
				}
			}
			if (list2.Count == 0)
			{
				return false;
			}
			Character val = list2[Random.Range(0, list2.Count)];
			HitData val2 = new HitData
			{
				m_attacker = ZDOID.None
			};
			switch (DamageElement)
			{
			case Element.Fire:
				val2.m_damage.m_fire = damage;
				break;
			case Element.Frost:
				val2.m_damage.m_frost = damage;
				break;
			case Element.Poison:
				val2.m_damage.m_poison = damage;
				break;
			case Element.Lightning:
				val2.m_damage.m_lightning = damage;
				break;
			case Element.Spirit:
				val2.m_damage.m_spirit = damage;
				break;
			}
			val.Damage(val2);
			return true;
		}

		private float GetDamage()
		{
			return DamageElement switch
			{
				Element.Fire => WardConfig.FireDamage.Value, 
				Element.Frost => WardConfig.FrostDamage.Value, 
				Element.Poison => WardConfig.PoisonDamage.Value, 
				Element.Lightning => WardConfig.LightningDamage.Value, 
				_ => WardConfig.SpiritDamage.Value, 
			};
		}
	}
	public class HealFlashStatusEffect : StatusEffect
	{
		public const string Name = "HW_HealFlash";

		private const string FxPrefabName = "vfx_Potion_health_medium";

		private static HealFlashStatusEffect? _cached;

		private GameObject? _vfxInstance;

		public static int Hash => StringExtensionMethods.GetStableHashCode("HW_HealFlash");

		public static void Register()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"[HealFlashSE] ObjectDB not ready");
				return;
			}
			if ((Object)(object)instance.GetStatusEffect(Hash) != (Object)null)
			{
				Plugin.Logger.LogInfo((object)$"[HealFlashSE] Already registered (hash={Hash})");
				return;
			}
			if ((Object)(object)_cached == (Object)null)
			{
				_cached = ScriptableObject.CreateInstance<HealFlashStatusEffect>();
				((Object)_cached).name = "HW_HealFlash";
				((StatusEffect)_cached).m_name = "$hw_heal_flash";
				((StatusEffect)_cached).m_ttl = 1f;
				((StatusEffect)_cached).m_icon = null;
				Object.DontDestroyOnLoad((Object)(object)_cached);
			}
			instance.m_StatusEffects.Add((StatusEffect)(object)_cached);
			Plugin.Logger.LogInfo((object)("[HealFlashSE] Registered " + $"(hash={Hash}, ODB#{((Object)instance).GetInstanceID()}, " + $"list#{instance.m_StatusEffects.GetHashCode()}, count={instance.m_StatusEffects.Count})"));
		}

		public override void Setup(Character character)
		{
			//IL_00b7: 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_011d: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Logger.LogInfo((object)("[HealFlashSE] Setup ENTER on " + (((character != null) ? ((Object)character).name : null) ?? "null") + " (type=" + ((object)this).GetType().Name + ")"));
			try
			{
				base.m_character = character;
				ZNetScene instance = ZNetScene.instance;
				GameObject val = ((instance != null) ? instance.GetPrefab("vfx_Potion_health_medium") : null);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Logger.LogWarning((object)"[HealFlashSE] Prefab not found: vfx_Potion_health_medium");
					return;
				}
				bool forceDisableInit = ZNetView.m_forceDisableInit;
				ZNetView.m_forceDisableInit = true;
				try
				{
					_vfxInstance = Object.Instantiate<GameObject>(val, ((Component)character).transform);
					_vfxInstance.transform.localPosition = Vector3.zero;
					_vfxInstance.transform.localRotation = Quaternion.identity;
					_vfxInstance.SetActive(true);
					Plugin.Logger.LogInfo((object)("[HealFlashSE] Instantiated " + ((Object)_vfxInstance).name + " " + $"world={_vfxInstance.transform.position} " + $"active={_vfxInstance.activeInHierarchy}"));
				}
				finally
				{
					ZNetView.m_forceDisableInit = forceDisableInit;
				}
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"[HealFlashSE] Setup exception: {arg}");
			}
		}

		public override void Stop()
		{
			_vfxInstance = null;
		}
	}
	public class HealingWardBehavior : WardBehavior
	{
		protected override float Interval => WardConfig.HealInterval.Value;

		protected override string? TickSoundPrefab => "sfx_dverger_heal_finish";

		protected override bool Tick()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			playFlash = false;
			bool result = false;
			List<Character> list = new List<Character>();
			Character.GetCharactersInRange(((Component)this).transform.position, Radius, list);
			foreach (Character item in list)
			{
				if (!((Object)(object)item == (Object)null) && !item.IsMonsterFaction(Time.time) && !(item.GetHealth() >= item.GetMaxHealth()))
				{
					result = true;
					item.GetSEMan().RemoveAllStatusEffects(true);
					item.Heal(WardConfig.HealAmount.Value, true);
					HealFlashStatusEffect.Register();
					item.GetSEMan().AddStatusEffect(HealFlashStatusEffect.Hash, true, 0, 0f);
				}
			}
			return result;
		}
	}
	public class HelpfulWardArea : MonoBehaviour, Hoverable, Interactable
	{
		public string m_name = "";

		public float m_radius = 10f;

		public bool m_enabledByDefault = true;

		public GameObject? m_enabledEffect;

		public MeshRenderer? m_model;

		public CircleProjector? m_areaMarker;

		public EffectList m_flashEffect = new EffectList();

		public EffectList m_activateEffect = new EffectList();

		public EffectList m_deactivateEffect = new EffectList();

		private ZNetView m_nview;

		private Piece m_piece;

		private bool m_flashAvailable = true;

		private void Awake()
		{
			m_nview = ((Component)this).GetComponent<ZNetView>();
			if (m_nview.IsValid())
			{
				m_piece = ((Component)this).GetComponent<Piece>();
				WearNTear component = ((Component)this).GetComponent<WearNTear>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_onDamaged = (Action)Delegate.Combine(component.m_onDamaged, new Action(OnDamaged));
				}
				if ((Object)(object)m_areaMarker != (Object)null)
				{
					((Component)m_areaMarker).gameObject.SetActive(false);
				}
				((MonoBehaviour)this).InvokeRepeating("UpdateStatus", 0f, 1f);
				m_nview.Register<long>("ToggleEnabled", (Action<long, long>)RPC_ToggleEnabled);
				m_nview.Register("FlashShield", (Action<long>)RPC_FlashShield);
				if (m_enabledByDefault && m_nview.IsOwner())
				{
					m_nview.GetZDO().Set(ZDOVars.s_enabled, true);
				}
			}
		}

		public bool IsEnabled()
		{
			if (!m_nview.IsValid())
			{
				return false;
			}
			return m_nview.GetZDO().GetBool(ZDOVars.s_enabled, false);
		}

		private void SetEnabled(bool enabled)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			m_nview.GetZDO().Set(ZDOVars.s_enabled, enabled);
			UpdateStatus();
			if (enabled)
			{
				m_activateEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1);
			}
			else
			{
				m_deactivateEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, (Transform)null, 1f, -1);
			}
		}

		private void UpdateStatus()
		{
			bool flag = IsEnabled();
			if ((Object)(object)m_enabledEffect != (Object)null)
			{
				m_enabledEffect.SetActive(flag);
			}
			m_flashAvailable = true;
			if (!((Object)(object)m_model != (Object)null))
			{
				return;
			}
			Material[] materials = ((Renderer)m_model).materials;
			foreach (Material val in materials)
			{
				if (flag)
				{
					val.EnableKeyword("_EMISSION");
				}
				else
				{
					val.DisableKeyword("_EMISSION");
				}
			}
		}

		public string GetHoverName()
		{
			return m_name;
		}

		public string GetHoverText()
		{
			if (!m_nview.IsValid() || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return "";
			}
			ShowAreaMarker();
			string text = (IsEnabled() ? "$piece_guardstone_active" : "$piece_guardstone_inactive");
			string text2 = m_name + " ( " + text + " )";
			if (m_piece.IsCreator())
			{
				string text3 = (IsEnabled() ? "$piece_guardstone_deactivate" : "$piece_guardstone_activate");
				text2 = text2 + "\n[<color=yellow><b>$KEY_Use</b></color>] " + text3;
			}
			return Localization.instance.Localize(text2);
		}

		public bool Interact(Humanoid human, bool hold, bool alt)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (hold)
			{
				return false;
			}
			if (!m_piece.IsCreator())
			{
				return false;
			}
			m_nview.InvokeRPC("ToggleEnabled", new object[1] { ((Player)human).GetPlayerID() });
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void RPC_ToggleEnabled(long uid, long playerID)
		{
			if (m_nview.IsOwner() && m_piece.GetCreator() == playerID)
			{
				SetEnabled(!IsEnabled());
			}
		}

		private void RPC_FlashShield(long uid)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			m_flashEffect.Create(((Component)this).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
		}

		public void ShowAreaMarker()
		{
			if (!((Object)(object)m_areaMarker == (Object)null))
			{
				((Component)m_areaMarker).gameObject.SetActive(true);
				((MonoBehaviour)this).CancelInvoke("HideMarker");
				((MonoBehaviour)this).Invoke("HideMarker", 0.5f);
			}
		}

		private void HideMarker()
		{
			if ((Object)(object)m_areaMarker != (Object)null)
			{
				((Component)m_areaMarker).gameObject.SetActive(false);
			}
		}

		private void OnDamaged()
		{
			if (IsEnabled() && m_flashAvailable)
			{
				m_flashAvailable = false;
				m_nview.InvokeRPC(ZNetView.Everybody, "FlashShield", Array.Empty<object>());
			}
		}
	}
	[BepInPlugin("Mordel2Berde.HelpfullWards", "HelpfullWards", "0.6.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "Mordel2Berde.HelpfullWards";

		public const string PluginName = "HelpfullWards";

		public const string PluginVersion = "0.6.0";

		internal static ManualLogSource Logger;

		private readonly Harmony _harmony = new Harmony("Mordel2Berde.HelpfullWards");

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			WardConfig.Init(((BaseUnityPlugin)this).Config);
			TranslationLoader.Load();
			PrefabManager.OnVanillaPrefabsAvailable += WardFactory.RegisterWards;
			PrefabManager.OnVanillaPrefabsAvailable += HealFlashStatusEffect.Register;
			_harmony.PatchAll();
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDBCopyOtherDBPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			HealFlashStatusEffect.Register();
		}
	}
	public class RepairWardBehavior : WardBehavior
	{
		protected override float Interval => WardConfig.RepairInterval.Value;

		private void Start()
		{
			DamagedPieceTracker.SeedFromAllInstances();
		}

		protected override bool Tick()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			playFlash = false;
			float num = Radius * Radius;
			Vector3 position = ((Component)this).transform.position;
			foreach (WearNTear item in DamagedPieceTracker.Damaged)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - position;
				if (((Vector3)(ref val)).sqrMagnitude > num)
				{
					continue;
				}
				ZNetView component = ((Component)item).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					component.InvokeRPC(component.GetZDO().GetOwner(), "RPC_Repair", Array.Empty<object>());
					Piece component2 = ((Component)item).GetComponent<Piece>();
					if (component2 != null)
					{
						component2.m_placeEffect.Create(((Component)item).transform.position, ((Component)item).transform.rotation, (Transform)null, 1f, -1);
					}
					return true;
				}
			}
			return false;
		}
	}
	public static class TranslationLoader
	{
		private static readonly string TranslationsDir = Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location), "Translations");

		public static void Load()
		{
			if (!Directory.Exists(TranslationsDir))
			{
				Directory.CreateDirectory(TranslationsDir);
				WriteDefaults();
			}
			string[] files = Directory.GetFiles(TranslationsDir, "*.json");
			foreach (string text in files)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				try
				{
					Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(text, Encoding.UTF8));
					if (dictionary == null || dictionary.Count == 0)
					{
						continue;
					}
					CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
					foreach (KeyValuePair<string, string> item in dictionary)
					{
						string key = item.Key;
						localization.AddTranslation(ref fileNameWithoutExtension, ref key, item.Value);
					}
					Plugin.Logger.LogInfo((object)$"[HelpfullWards] Translations loaded: {fileNameWithoutExtension} ({dictionary.Count} entries)");
				}
				catch (Exception ex)
				{
					Plugin.Logger.LogError((object)("[HelpfullWards] Error reading " + text + ": " + ex.Message));
				}
			}
		}

		private static void WriteDefaults()
		{
			Write("English", new Dictionary<string, string>
			{
				{ "hw_ward_fire", "Fire Ward" },
				{ "hw_ward_fire_desc", "Deals fire damage periodically to enemies within range." },
				{ "hw_ward_frost", "Frost Ward" },
				{ "hw_ward_frost_desc", "Deals frost damage periodically to enemies within range." },
				{ "hw_ward_poison", "Poison Ward" },
				{ "hw_ward_poison_desc", "Deals poison damage periodically to enemies within range." },
				{ "hw_ward_lightning", "Lightning Ward" },
				{ "hw_ward_lightning_desc", "Deals lightning damage periodically to enemies within range." },
				{ "hw_ward_spirit", "Spirit Ward" },
				{ "hw_ward_spirit_desc", "Deals spirit damage periodically to enemies within range." },
				{ "hw_ward_repair", "Repair Ward" },
				{ "hw_ward_repair_desc", "Automatically repairs damaged constructions within range." },
				{ "hw_ward_healing", "Healing Ward" },
				{ "hw_ward_healing_desc", "Periodically heals players within range." }
			});
			Write("French", new Dictionary<string, string>
			{
				{ "hw_ward_fire", "Balise de feu" },
				{ "hw_ward_fire_desc", "Inflige des dégâts de feu périodiques aux ennemis dans son rayon." },
				{ "hw_ward_frost", "Balise de givre" },
				{ "hw_ward_frost_desc", "Inflige des dégâts de givre périodiques aux ennemis dans son rayon." },
				{ "hw_ward_poison", "Balise de poison" },
				{ "hw_ward_poison_desc", "Inflige des dégâts de poison périodiques aux ennemis dans son rayon." },
				{ "hw_ward_lightning", "Balise de foudre" },
				{ "hw_ward_lightning_desc", "Inflige des dégâts de foudre périodiques aux ennemis dans son rayon." },
				{ "hw_ward_spirit", "Balise spirituelle" },
				{ "hw_ward_spirit_desc", "Inflige des dégâts d'esprit périodiques aux ennemis dans son rayon." },
				{ "hw_ward_repair", "Balise de Réparation" },
				{ "hw_ward_repair_desc", "Répare automatiquement les constructions endommagées dans son rayon." },
				{ "hw_ward_healing", "Balise de Soin" },
				{ "hw_ward_healing_desc", "Soigne périodiquement les joueurs dans son rayon." }
			});
		}

		private static void Write(string language, Dictionary<string, string> entries)
		{
			string text = Path.Combine(TranslationsDir, language + ".json");
			if (!File.Exists(text))
			{
				File.WriteAllText(text, JsonConvert.SerializeObject((object)entries, (Formatting)1), Encoding.UTF8);
				Plugin.Logger.LogInfo((object)("[HelpfullWards] Translation file created: " + text));
			}
		}
	}
	public abstract class WardBehavior : MonoBehaviour
	{
		private const float MaxCharge = 1f;

		private ZNetView _nview;

		private HelpfulWardArea _wardArea;

		private Light? _wardLight;

		private float _baseIntensity;

		private float _charge = 1f;

		private float _rechargeDuration;

		public float Radius;

		protected bool playFlash = true;

		protected abstract float Interval { get; }

		protected virtual string? TickSoundPrefab => null;

		protected abstract bool Tick();

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			_wardArea = ((Component)this).GetComponent<HelpfulWardArea>();
			_wardLight = ((Component)this).GetComponentInChildren<Light>();
			_baseIntensity = (((Object)(object)_wardLight != (Object)null) ? _wardLight.intensity : 1f);
			_nview.Register<float>("HW_Discharge", (Action<long, float>)RPC_Discharge);
		}

		private void Update()
		{
			if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner() && _wardArea.IsEnabled() && !(_charge < 1f) && Tick())
			{
				float num = Interval + (Random.Range(0f, 2f * Interval) - Interval) / 10f;
				_nview.InvokeRPC(ZNetView.Everybody, "HW_Discharge", new object[1] { num });
			}
		}

		private void RPC_Discharge(long sender, float duration)
		{
			PlayTickEffect();
			_charge = 0f;
			_rechargeDuration = Mathf.Max(0.01f, duration);
			if ((Object)(object)_wardLight != (Object)null)
			{
				_wardLight.intensity = 0f;
			}
			((MonoBehaviour)this).StopCoroutine("Recharge");
			((MonoBehaviour)this).StartCoroutine("Recharge");
		}

		private IEnumerator Recharge()
		{
			float elapsed = 0f;
			while (elapsed < _rechargeDuration)
			{
				elapsed += Time.deltaTime;
				_charge = Mathf.Min(1f, elapsed / _rechargeDuration);
				if ((Object)(object)_wardLight != (Object)null)
				{
					_wardLight.intensity = _baseIntensity * _charge;
				}
				yield return null;
			}
			_charge = 1f;
			if ((Object)(object)_wardLight != (Object)null)
			{
				_wardLight.intensity = _baseIntensity;
			}
		}

		private void PlayTickEffect()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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 (playFlash)
			{
				_wardArea.m_flashEffect.Create(((Component)this).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
			}
			if (TickSoundPrefab != null)
			{
				ZNetScene instance = ZNetScene.instance;
				GameObject val = ((instance != null) ? instance.GetPrefab(TickSoundPrefab) : null);
				if ((Object)(object)val != (Object)null)
				{
					Object.Instantiate<GameObject>(val, ((Component)this).transform.position, Quaternion.identity);
				}
			}
		}
	}
	public static class WardConfig
	{
		public static ConfigEntry<float> ElementalTickInterval;

		public static ConfigEntry<string> ElementalExcludedFactions;

		public static ConfigEntry<float> FireDamage;

		public static ConfigEntry<float> FireRadius;

		public static ConfigEntry<string> FireIngredients;

		public static ConfigEntry<float> FrostDamage;

		public static ConfigEntry<float> FrostRadius;

		public static ConfigEntry<string> FrostIngredients;

		public static ConfigEntry<float> PoisonDamage;

		public static ConfigEntry<float> PoisonRadius;

		public static ConfigEntry<string> PoisonIngredients;

		public static ConfigEntry<float> LightningDamage;

		public static ConfigEntry<float> LightningRadius;

		public static ConfigEntry<string> LightningIngredients;

		public static ConfigEntry<float> SpiritDamage;

		public static ConfigEntry<float> SpiritRadius;

		public static ConfigEntry<string> SpiritIngredients;

		public static ConfigEntry<float> RepairInterval;

		public static ConfigEntry<float> RepairRadius;

		public static ConfigEntry<string> RepairIngredients;

		public static ConfigEntry<float> HealInterval;

		public static ConfigEntry<float> HealAmount;

		public static ConfigEntry<float> HealRadius;

		public static ConfigEntry<string> HealIngredients;

		private const string IngredientsDesc = "Crafting ingredients (comma-separated, format: ItemName:Amount).";

		public static void Init(ConfigFile cfg)
		{
			ElementalTickInterval = cfg.Bind<float>("Elemental", "TickInterval", 3f, "Seconds between each elemental damage tick.");
			ElementalExcludedFactions = cfg.Bind<string>("Elemental", "ExcludedFactions", "Players,Dverger", "Factions excluded from elemental damage (comma-separated).\nPossible values: Players, AnimaI, ForestMonsters, Undead, Demon, MountainMonsters, SeaMonsters, PlainsMonsters, Boss");
			FireDamage = cfg.Bind<float>("Ward_Fire", "Damage", 10f, "Fire damage per tick.");
			FireRadius = cfg.Bind<float>("Ward_Fire", "Radius", 32f, "Fire ward radius (meters).");
			FireIngredients = cfg.Bind<string>("Ward_Fire", "Ingredients", "FineWood:5,TrophySurtling:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			FrostDamage = cfg.Bind<float>("Ward_Frost", "Damage", 10f, "Frost damage per tick.");
			FrostRadius = cfg.Bind<float>("Ward_Frost", "Radius", 32f, "Frost ward radius (meters).");
			FrostIngredients = cfg.Bind<string>("Ward_Frost", "Ingredients", "FineWood:5,TrophyHatchling:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			PoisonDamage = cfg.Bind<float>("Ward_Poison", "Damage", 10f, "Poison damage per tick.");
			PoisonRadius = cfg.Bind<float>("Ward_Poison", "Radius", 32f, "Poison ward radius (meters).");
			PoisonIngredients = cfg.Bind<string>("Ward_Poison", "Ingredients", "FineWood:5,TrophyBlob:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			LightningDamage = cfg.Bind<float>("Ward_Lightning", "Damage", 10f, "Lightning damage per tick.");
			LightningRadius = cfg.Bind<float>("Ward_Lightning", "Radius", 32f, "Lightning ward radius (meters).");
			LightningIngredients = cfg.Bind<string>("Ward_Lightning", "Ingredients", "FineWood:5,Crystal:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			SpiritDamage = cfg.Bind<float>("Ward_Spirit", "Damage", 10f, "Spirit damage per tick.");
			SpiritRadius = cfg.Bind<float>("Ward_Spirit", "Radius", 32f, "Spirit ward radius (meters).");
			SpiritIngredients = cfg.Bind<string>("Ward_Spirit", "Ingredients", "FineWood:5,TrophyGhost:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			RepairInterval = cfg.Bind<float>("Ward_Repair", "Interval", 10f, "Seconds between each automatic repair.");
			RepairRadius = cfg.Bind<float>("Ward_Repair", "Radius", 32f, "Repair ward radius (meters).");
			RepairIngredients = cfg.Bind<string>("Ward_Repair", "Ingredients", "FineWood:5,YggdrasilWood:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
			HealInterval = cfg.Bind<float>("Ward_Healing", "Interval", 10f, "Seconds between each automatic heal.");
			HealAmount = cfg.Bind<float>("Ward_Healing", "HealAmount", 10f, "Hit points restored per tick.");
			HealRadius = cfg.Bind<float>("Ward_Healing", "Radius", 32f, "Healing ward radius (meters).");
			HealIngredients = cfg.Bind<string>("Ward_Healing", "Ingredients", "FineWood:5,TrophyGreydwarfShaman:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount).");
		}

		public static HashSet<Faction> GetExcludedFactions()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Faction> hashSet = new HashSet<Faction>();
			string[] array = ElementalExcludedFactions.Value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (Enum.TryParse<Faction>(array[i].Trim(), out Faction result))
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}

		public static RequirementConfig[] ParseIngredients(string value)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[1] { ':' });
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result))
				{
					list.Add(new RequirementConfig
					{
						Item = array2[0].Trim(),
						Amount = result,
						Recover = true
					});
				}
			}
			return list.ToArray();
		}
	}
	public static class WardFactory
	{
		private static readonly Dictionary<string, Dictionary<string, string>> _color_properties = new Dictionary<string, Dictionary<string, string>>
		{
			{
				"default",
				new Dictionary<string, string> { { "Guardstone_OdenGlow_mat", "_EmissionColor" } }
			},
			{
				"sparcs",
				new Dictionary<string, string> { { "gnista", "_Color" } }
			}
		};

		public static void RegisterWards()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			RegisterElemental("piece_ward_fire", "$hw_ward_fire", "$hw_ward_fire_desc", new Color(1f, 0.15f, 0f), ElementalWardBehavior.Element.Fire, WardConfig.FireRadius.Value, WardConfig.ParseIngredients(WardConfig.FireIngredients.Value));
			RegisterElemental("piece_ward_frost", "$hw_ward_frost", "$hw_ward_frost_desc", new Color(0.85f, 0.95f, 1f), ElementalWardBehavior.Element.Frost, WardConfig.FrostRadius.Value, WardConfig.ParseIngredients(WardConfig.FrostIngredients.Value));
			RegisterElemental("piece_ward_poison", "$hw_ward_poison", "$hw_ward_poison_desc", new Color(0.1f, 0.9f, 0.1f), ElementalWardBehavior.Element.Poison, WardConfig.PoisonRadius.Value, WardConfig.ParseIngredients(WardConfig.PoisonIngredients.Value));
			RegisterElemental("piece_ward_lightning", "$hw_ward_lightning", "$hw_ward_lightning_desc", new Color(0.2f, 0.75f, 1f), ElementalWardBehavior.Element.Lightning, WardConfig.LightningRadius.Value, WardConfig.ParseIngredients(WardConfig.LightningIngredients.Value));
			RegisterElemental("piece_ward_spirit", "$hw_ward_spirit", "$hw_ward_spirit_desc", new Color(0.65f, 0.1f, 1f), ElementalWardBehavior.Element.Spirit, WardConfig.SpiritRadius.Value, WardConfig.ParseIngredients(WardConfig.SpiritIngredients.Value));
			RegisterSpecial<RepairWardBehavior>("piece_ward_repair", "$hw_ward_repair", "$hw_ward_repair_desc", new Color(1f, 0.45f, 0f), WardConfig.RepairRadius.Value, WardConfig.ParseIngredients(WardConfig.RepairIngredients.Value));
			RegisterSpecial<HealingWardBehavior>("piece_ward_healing", "$hw_ward_healing", "$hw_ward_healing_desc", new Color(1f, 0.35f, 0.65f), WardConfig.HealRadius.Value, WardConfig.ParseIngredients(WardConfig.HealIngredients.Value));
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterWards;
		}

		private static void RegisterElemental(string prefabName, string displayName, string desc, Color lightColor, ElementalWardBehavior.Element element, float radius, params RequirementConfig[] reqs)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CloneWard(prefabName, lightColor, displayName, radius);
			if (!((Object)(object)val == (Object)null))
			{
				val.AddComponent<ElementalWardBehavior>().DamageElement = element;
				val.GetComponent<ElementalWardBehavior>().Radius = radius;
				RegisterPiece(val, displayName, desc, reqs);
			}
		}

		private static void RegisterSpecial<T>(string prefabName, string displayName, string desc, Color lightColor, float radius, params RequirementConfig[] reqs) where T : WardBehavior
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CloneWard(prefabName, lightColor, displayName, radius);
			if (!((Object)(object)val == (Object)null))
			{
				val.AddComponent<T>();
				val.GetComponent<T>().Radius = radius;
				RegisterPiece(val, displayName, desc, reqs);
			}
		}

		private static GameObject? CloneWard(string name, Color lightColor, string displayName, float radius)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(name, "guard_stone");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogError((object)("[HelpfullWards] Failed to clone guard_stone for: " + name));
				return null;
			}
			PrivateArea component = val.GetComponent<PrivateArea>();
			HelpfulWardArea helpfulWardArea = val.AddComponent<HelpfulWardArea>();
			helpfulWardArea.m_name = displayName;
			helpfulWardArea.m_radius = radius;
			helpfulWardArea.m_enabledByDefault = true;
			helpfulWardArea.m_enabledEffect = component.m_enabledEffect;
			helpfulWardArea.m_model = component.m_model;
			helpfulWardArea.m_areaMarker = component.m_areaMarker;
			helpfulWardArea.m_flashEffect = component.m_flashEffect;
			helpfulWardArea.m_activateEffect = component.m_activateEffect;
			helpfulWardArea.m_deactivateEffect = component.m_deactivateEffect;
			Object.DestroyImmediate((Object)(object)component);
			Light[] componentsInChildren = val.GetComponentsInChildren<Light>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].color = lightColor;
			}
			Renderer[] componentsInChildren2 = val.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren2)
			{
				if (!_color_properties.ContainsKey(((Object)val2).name))
				{
					continue;
				}
				Material[] sharedMaterials = val2.sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if (!((Object)(object)sharedMaterials[j] == (Object)null) && _color_properties[((Object)val2).name].ContainsKey(((Object)sharedMaterials[j]).name))
					{
						string text = _color_properties[((Object)val2).name][((Object)sharedMaterials[j]).name];
						Material val3 = new Material(sharedMaterials[j]);
						val3.SetColor(text, lightColor);
						sharedMaterials[j] = val3;
						flag = true;
					}
				}
				if (flag)
				{
					val2.sharedMaterials = sharedMaterials;
				}
			}
			return val;
		}

		private static void RegisterPiece(GameObject go, string displayName, string desc, RequirementConfig[] reqs)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			PieceManager.Instance.AddPiece(new CustomPiece(go, true, new PieceConfig
			{
				Name = displayName,
				Description = desc,
				PieceTable = "_HammerPieceTable",
				Category = "Misc",
				CraftingStation = "piece_workbench",
				Requirements = reqs
			}));
		}
	}
}