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 Damaged = new HashSet(); 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 list = new List(); AppendCount(go, "ParticleSystem", list); AppendCount(go, "Light", list); AppendCount(go, "AudioSource", list); AppendCount(go, "ZSFX", list); AppendCount(go, "TrailRenderer", list); AppendCount(go, "LineRenderer", list); AppendCount(go, "MeshRenderer", list); AppendCount(go, "SkinnedMeshRenderer", list); if ((Object)(object)go.GetComponent() != (Object)null) { list.Add("ZNetView"); } if ((Object)(object)go.GetComponent() != (Object)null) { list.Add("Aoe"); } if ((Object)(object)go.GetComponent() != (Object)null) { list.Add("TimedDestruction"); } StatusEffect component = go.GetComponent(); 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(GameObject go, string label, List parts) where T : Component { int num = go.GetComponents().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? _excluded; protected override float Interval => WardConfig.ElementalTickInterval.Value; private static HashSet 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 list = new List(); Character.GetCharactersInRange(((Component)this).transform.position, Radius, list); List list2 = new List(); 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(); ((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(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 list = new List(); 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(); if (m_nview.IsValid()) { m_piece = ((Component)this).GetComponent(); WearNTear component = ((Component)this).GetComponent(); 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("ToggleEnabled", (Action)RPC_ToggleEnabled); m_nview.Register("FlashShield", (Action)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[$KEY_Use] " + 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()); } } } [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(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { component.InvokeRPC(component.GetZDO().GetOwner(), "RPC_Repair", Array.Empty()); Piece component2 = ((Component)item).GetComponent(); 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 dictionary = JsonConvert.DeserializeObject>(File.ReadAllText(text, Encoding.UTF8)); if (dictionary == null || dictionary.Count == 0) { continue; } CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); foreach (KeyValuePair 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 { { "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 { { "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 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(); _wardArea = ((Component)this).GetComponent(); _wardLight = ((Component)this).GetComponentInChildren(); _baseIntensity = (((Object)(object)_wardLight != (Object)null) ? _wardLight.intensity : 1f); _nview.Register("HW_Discharge", (Action)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(val, ((Component)this).transform.position, Quaternion.identity); } } } } public static class WardConfig { public static ConfigEntry ElementalTickInterval; public static ConfigEntry ElementalExcludedFactions; public static ConfigEntry FireDamage; public static ConfigEntry FireRadius; public static ConfigEntry FireIngredients; public static ConfigEntry FrostDamage; public static ConfigEntry FrostRadius; public static ConfigEntry FrostIngredients; public static ConfigEntry PoisonDamage; public static ConfigEntry PoisonRadius; public static ConfigEntry PoisonIngredients; public static ConfigEntry LightningDamage; public static ConfigEntry LightningRadius; public static ConfigEntry LightningIngredients; public static ConfigEntry SpiritDamage; public static ConfigEntry SpiritRadius; public static ConfigEntry SpiritIngredients; public static ConfigEntry RepairInterval; public static ConfigEntry RepairRadius; public static ConfigEntry RepairIngredients; public static ConfigEntry HealInterval; public static ConfigEntry HealAmount; public static ConfigEntry HealRadius; public static ConfigEntry HealIngredients; private const string IngredientsDesc = "Crafting ingredients (comma-separated, format: ItemName:Amount)."; public static void Init(ConfigFile cfg) { ElementalTickInterval = cfg.Bind("Elemental", "TickInterval", 3f, "Seconds between each elemental damage tick."); ElementalExcludedFactions = cfg.Bind("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("Ward_Fire", "Damage", 10f, "Fire damage per tick."); FireRadius = cfg.Bind("Ward_Fire", "Radius", 32f, "Fire ward radius (meters)."); FireIngredients = cfg.Bind("Ward_Fire", "Ingredients", "FineWood:5,TrophySurtling:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); FrostDamage = cfg.Bind("Ward_Frost", "Damage", 10f, "Frost damage per tick."); FrostRadius = cfg.Bind("Ward_Frost", "Radius", 32f, "Frost ward radius (meters)."); FrostIngredients = cfg.Bind("Ward_Frost", "Ingredients", "FineWood:5,TrophyHatchling:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); PoisonDamage = cfg.Bind("Ward_Poison", "Damage", 10f, "Poison damage per tick."); PoisonRadius = cfg.Bind("Ward_Poison", "Radius", 32f, "Poison ward radius (meters)."); PoisonIngredients = cfg.Bind("Ward_Poison", "Ingredients", "FineWood:5,TrophyBlob:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); LightningDamage = cfg.Bind("Ward_Lightning", "Damage", 10f, "Lightning damage per tick."); LightningRadius = cfg.Bind("Ward_Lightning", "Radius", 32f, "Lightning ward radius (meters)."); LightningIngredients = cfg.Bind("Ward_Lightning", "Ingredients", "FineWood:5,Crystal:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); SpiritDamage = cfg.Bind("Ward_Spirit", "Damage", 10f, "Spirit damage per tick."); SpiritRadius = cfg.Bind("Ward_Spirit", "Radius", 32f, "Spirit ward radius (meters)."); SpiritIngredients = cfg.Bind("Ward_Spirit", "Ingredients", "FineWood:5,TrophyGhost:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); RepairInterval = cfg.Bind("Ward_Repair", "Interval", 10f, "Seconds between each automatic repair."); RepairRadius = cfg.Bind("Ward_Repair", "Radius", 32f, "Repair ward radius (meters)."); RepairIngredients = cfg.Bind("Ward_Repair", "Ingredients", "FineWood:5,YggdrasilWood:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); HealInterval = cfg.Bind("Ward_Healing", "Interval", 10f, "Seconds between each automatic heal."); HealAmount = cfg.Bind("Ward_Healing", "HealAmount", 10f, "Hit points restored per tick."); HealRadius = cfg.Bind("Ward_Healing", "Radius", 32f, "Healing ward radius (meters)."); HealIngredients = cfg.Bind("Ward_Healing", "Ingredients", "FineWood:5,TrophyGreydwarfShaman:11,Eitr:1", "Crafting ingredients (comma-separated, format: ItemName:Amount)."); } public static HashSet GetExcludedFactions() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); string[] array = ElementalExcludedFactions.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (Enum.TryParse(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 list = new List(); 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> _color_properties = new Dictionary> { { "default", new Dictionary { { "Guardstone_OdenGlow_mat", "_EmissionColor" } } }, { "sparcs", new Dictionary { { "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("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("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().DamageElement = element; val.GetComponent().Radius = radius; RegisterPiece(val, displayName, desc, reqs); } } private static void RegisterSpecial(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(); val.GetComponent().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(); HelpfulWardArea helpfulWardArea = val.AddComponent(); 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(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].color = lightColor; } Renderer[] componentsInChildren2 = val.GetComponentsInChildren(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 })); } } }