plugins\G3A3\G3A3.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 G3A3
{
	[BepInPlugin("g3a3.g3a3", "G3A3", "1.7.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Tameable), "UnsummonMaxInstances")]
		private static class ArcherSummonCapPatch
		{
			private static void Prefix(Tameable __instance, ref int maxInstances)
			{
				if (!((Object)(object)__instance == (Object)null) && maxInstances >= 1 && ((Object)__instance).name != null && ((Object)__instance).name.StartsWith("G3A3_SkeletonArcher", StringComparison.Ordinal))
				{
					maxInstances *= 2;
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		private static class InventoryGuiDoCraftingPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				FieldInfo upgrade = AccessTools.Field(typeof(SharedData), "m_upgradeChance");
				FieldInfo brk = AccessTools.Field(typeof(SharedData), "m_breakChance");
				MethodInfo ovUpgrade = AccessTools.Method(typeof(Plugin), "OverrideUpgradeChance", (Type[])null, (Type[])null);
				MethodInfo ovBreak = AccessTools.Method(typeof(Plugin), "OverrideBreakChance", (Type[])null, (Type[])null);
				foreach (CodeInstruction code in instructions)
				{
					yield return code;
					if (CodeInstructionExtensions.LoadsField(code, upgrade, false))
					{
						yield return new CodeInstruction(OpCodes.Call, (object)ovUpgrade);
					}
					else if (CodeInstructionExtensions.LoadsField(code, brk, false))
					{
						yield return new CodeInstruction(OpCodes.Call, (object)ovBreak);
					}
				}
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDBAwakePatch
		{
			private static void Postfix()
			{
				ApplyRestedXpSettings();
			}
		}

		[HarmonyPatch(typeof(SE_Rested), "Setup")]
		private static class SERestedSetupPatch
		{
			private static void Postfix(SE_Rested __instance)
			{
				ApplyRestedXpModifier((StatusEffect)(object)__instance);
			}
		}

		[HarmonyPatch(typeof(EnvMan), "Awake")]
		private static class EnvManAwakePatch
		{
			private static void Postfix(EnvMan __instance)
			{
				__instance.m_dayLengthSec = TotalSeconds;
				_hasStarPeriodState = false;
				_knownStarPeriod = -1;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "FixedUpdate")]
		private static class EnvManFixedUpdatePatch
		{
			private static readonly FieldInfo NightField = AccessTools.Field(typeof(EnvMan), "s_isNight");

			private static readonly FieldInfo DayField = AccessTools.Field(typeof(EnvMan), "s_isDay");

			private static readonly FieldInfo AfternoonField = AccessTools.Field(typeof(EnvMan), "s_isAfternoon");

			private static readonly FieldInfo CanSleepField = AccessTools.Field(typeof(EnvMan), "s_canSleep");

			private static readonly FieldInfo SkipTimeField = AccessTools.Field(typeof(EnvMan), "m_skipTime");

			private static void Prefix(EnvMan __instance)
			{
				__instance.m_dayLengthSec = TotalSeconds;
			}

			private static void Postfix(EnvMan __instance)
			{
				SnapSmoothDayFraction(__instance);
				DayField?.SetValue(null, AccessTools.Method(typeof(EnvMan), "CalculateDay", (Type[])null, (Type[])null).Invoke(__instance, null));
				NightField?.SetValue(null, AccessTools.Method(typeof(EnvMan), "CalculateNight", (Type[])null, (Type[])null).Invoke(__instance, null));
				AfternoonField?.SetValue(null, AccessTools.Method(typeof(EnvMan), "CalculateAfternoon", (Type[])null, (Type[])null).Invoke(__instance, null));
				CanSleepField?.SetValue(null, AccessTools.Method(typeof(EnvMan), "CalculateCanSleep", (Type[])null, (Type[])null).Invoke(__instance, null));
				object obj = SkipTimeField?.GetValue(__instance);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) == 0)
				{
					RefreshLoadedCreatureStars();
				}
			}
		}

		[HarmonyPatch(typeof(EnvMan), "GetDayFraction")]
		private static class EnvManGetDayFractionPatch
		{
			private static bool Prefix(EnvMan __instance, ref float __result)
			{
				__result = GetTrueDisplayFraction(__instance);
				return false;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "RescaleDayFraction")]
		private static class EnvManRescaleDayFractionPatch
		{
			private static bool Prefix(float fraction, ref float __result)
			{
				__result = RescaleRawFraction(fraction);
				return false;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "GetMorningStartSec")]
		private static class EnvManGetMorningStartSecPatch
		{
			private static bool Prefix(EnvMan __instance, int day, ref double __result)
			{
				long num = ((__instance.m_dayLengthSec > 0) ? __instance.m_dayLengthSec : TotalSeconds);
				__result = (double)day * (double)num + (double)((float)num * MorningRawFraction());
				return false;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "SkipToMorning")]
		private static class EnvManSkipToMorningPatch
		{
			private static bool Prefix(EnvMan __instance, ref bool ___m_skipTime, ref double ___m_skipToTime, ref double ___m_timeSkipSpeed)
			{
				__instance.m_dayLengthSec = TotalSeconds;
				float num = MorningRawFraction();
				double timeSeconds = ZNet.instance.GetTimeSeconds();
				double num2 = timeSeconds - (double)((float)__instance.m_dayLengthSec * num);
				int day = __instance.GetDay(num2);
				double morningStartSec = __instance.GetMorningStartSec(day + 1);
				if (morningStartSec <= timeSeconds + 1.0)
				{
					morningStartSec = __instance.GetMorningStartSec(day + 2);
				}
				___m_skipTime = true;
				___m_skipToTime = morningStartSec;
				___m_timeSkipSpeed = Math.Max(1.0, morningStartSec - timeSeconds) / 12.0;
				SnapSmoothDayFraction(__instance);
				float trueDisplayFraction = GetTrueDisplayFraction(__instance);
				Plugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)$"SkipToMorning now={timeSeconds:F1} dawnRaw={num:F4} day={day} target={morningStartSec:F1} displayNow={trueDisplayFraction:F3} (wake needs >0.25)");
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(EnvMan), "UpdateTimeSkip")]
		private static class EnvManUpdateTimeSkipPatch
		{
			private static void Postfix(EnvMan __instance)
			{
				SnapSmoothDayFraction(__instance);
			}
		}

		[HarmonyPatch(typeof(Turret), "Awake")]
		private static class TurretAwakePatch
		{
			private static void Postfix(Turret __instance)
			{
				ApplyBallistaViewDistance(__instance);
			}
		}

		[HarmonyPatch(typeof(Minimap), "Explore", new Type[]
		{
			typeof(Vector3),
			typeof(float)
		})]
		private static class MinimapExploreRadiusPatch
		{
			private static void Prefix(ref float radius)
			{
				radius *= GetMapExploreMultiplier();
			}
		}

		[HarmonyPatch(typeof(SpawnSystem), "Spawn")]
		private static class SpawnSystemSpawnPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0074: Expected O, but got Unknown
				//IL_007e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Expected O, but got Unknown
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_009c: Expected O, but got Unknown
				MethodInfo methodInfo = AccessTools.Method(typeof(Plugin), "ApplyNightStarsFromObject", new Type[2]
				{
					typeof(GameObject),
					typeof(bool)
				}, (Type[])null);
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				int num = list.FindLastIndex((CodeInstruction code) => code.opcode == OpCodes.Ret);
				list.Insert(num, new CodeInstruction(OpCodes.Ldloc_0, (object)null));
				list.Insert(num + 1, new CodeInstruction(OpCodes.Ldarg_3, (object)null));
				list.Insert(num + 2, new CodeInstruction(OpCodes.Call, (object)methodInfo));
				return list;
			}
		}

		[HarmonyPatch(typeof(SpawnArea), "SpawnOne")]
		private static class SpawnAreaSpawnOnePatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_006c: Expected O, but got Unknown
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: Expected O, but got Unknown
				MethodInfo methodInfo = AccessTools.Method(typeof(Plugin), "ApplyNightStars", new Type[1] { typeof(Character) }, (Type[])null);
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				int num = list.FindLastIndex((CodeInstruction code) => code.opcode == OpCodes.Ret);
				list.Insert(num, new CodeInstruction(OpCodes.Ldloc_S, (object)5));
				list.Insert(num + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo));
				return list;
			}
		}

		[HarmonyPatch(typeof(CreatureSpawner), "Spawn")]
		private static class CreatureSpawnerSpawnPatch
		{
			private static void Postfix(ZNetView __result)
			{
				if (!((Object)(object)__result == (Object)null))
				{
					ApplyNightStars(FindCharacter(((Component)__result).gameObject), newlySpawned: true);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Start")]
		private static class CharacterStartPatch
		{
			private static void Postfix(Character __instance)
			{
				SyncCreatureNightStars(__instance);
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_SetTamed")]
		private static class CharacterRpcSetTamedPatch
		{
			private static void Postfix(Character __instance, bool tamed)
			{
				if (tamed)
				{
					KeepTamedNightStars(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Tameable), "Tame")]
		private static class TameableTamePatch
		{
			private static void Postfix(Tameable __instance)
			{
				KeepTamedNightStars(((Component)__instance).GetComponent<Character>());
			}
		}

		[HarmonyPatch(typeof(Character), "SetupMaxHealth")]
		private static class CharacterSetupMaxHealthPatch
		{
			private static void Postfix(Character __instance)
			{
				if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer())
				{
					float extraHealthMultiplier = GetExtraHealthMultiplier(__instance.GetLevel());
					if (!(extraHealthMultiplier < 0f))
					{
						__instance.SetMaxHealth(__instance.GetMaxHealthBase() * extraHealthMultiplier);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Attack), "GetLevelDamageFactor")]
		private static class AttackGetLevelDamageFactorPatch
		{
			private static readonly FieldInfo AttackCharacterField = AccessTools.Field(typeof(Attack), "m_character");

			private static void Postfix(Attack __instance, ref float __result)
			{
				if (__instance != null && !(AttackCharacterField == null))
				{
					object? value = AttackCharacterField.GetValue(__instance);
					Character val = (Character)((value is Character) ? value : null);
					if (!((Object)(object)val == (Object)null) && TryGetExtraDamageMultiplier(val.GetLevel(), out var multiplier))
					{
						__result = multiplier;
					}
				}
			}
		}

		[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
		private static class CharacterDropGenerateDropListPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e1: Expected O, but got Unknown
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f6: Expected O, but got Unknown
				//IL_0101: Unknown result type (might be due to invalid IL or missing references)
				//IL_010b: Expected O, but got Unknown
				//IL_0116: Unknown result type (might be due to invalid IL or missing references)
				//IL_0120: Expected O, but got Unknown
				//IL_012b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0135: Expected O, but got Unknown
				MethodInfo methodInfo = AccessTools.Method(typeof(Mathf), "Pow", new Type[2]
				{
					typeof(float),
					typeof(float)
				}, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "OverrideLootMultiplier", (Type[])null, (Type[])null);
				FieldInfo fieldInfo = AccessTools.Field(typeof(CharacterDrop), "m_character");
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				for (int i = 0; i < list.Count; i++)
				{
					if (!CodeInstructionExtensions.Calls(list[i], methodInfo))
					{
						continue;
					}
					int num = -1;
					for (int j = i + 1; j < Math.Min(i + 8, list.Count); j++)
					{
						if (list[j].opcode == OpCodes.Stloc_1)
						{
							num = j;
							break;
						}
					}
					if (num >= 0)
					{
						list.Insert(num + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
						list.Insert(num + 2, new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo));
						list.Insert(num + 3, new CodeInstruction(OpCodes.Ldloc_1, (object)null));
						list.Insert(num + 4, new CodeInstruction(OpCodes.Call, (object)methodInfo2));
						list.Insert(num + 5, new CodeInstruction(OpCodes.Stloc_1, (object)null));
					}
					break;
				}
				return list;
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		private static class EnemyHudUpdateHudsPatch
		{
			private const string ExtraStarPrefix = "g3a3_star_";

			private const int MaxExtraStars = 2;

			private static readonly FieldInfo HudsField = AccessTools.Field(typeof(EnemyHud), "m_huds");

			private static readonly Type HudDataType = AccessTools.Inner(typeof(EnemyHud), "HudData");

			private static readonly FieldInfo HudCharacterField = ((HudDataType == null) ? null : AccessTools.Field(HudDataType, "m_character"));

			private static readonly FieldInfo HudGuiField = ((HudDataType == null) ? null : AccessTools.Field(HudDataType, "m_gui"));

			private static readonly FieldInfo HudLevel2Field = ((HudDataType == null) ? null : AccessTools.Field(HudDataType, "m_level2"));

			private static readonly FieldInfo HudLevel3Field = ((HudDataType == null) ? null : AccessTools.Field(HudDataType, "m_level3"));

			private const float StarPitch = 18f;

			private static void Postfix(EnemyHud __instance)
			{
				if ((Object)(object)__instance == (Object)null || HudsField == null || HudDataType == null || !(HudsField.GetValue(__instance) is IDictionary dictionary))
				{
					return;
				}
				foreach (DictionaryEntry item in dictionary)
				{
					ApplyExtraStars(item.Value);
				}
			}

			private static void ApplyExtraStars(object hud)
			{
				if (hud == null)
				{
					return;
				}
				object? value = HudCharacterField.GetValue(hud);
				Character val = (Character)((value is Character) ? value : null);
				object? value2 = HudGuiField.GetValue(hud);
				GameObject val2 = (GameObject)((value2 is GameObject) ? value2 : null);
				object? value3 = HudLevel2Field.GetValue(hud);
				RectTransform val3 = (RectTransform)((value3 is RectTransform) ? value3 : null);
				RectTransform val4 = (RectTransform)((HudLevel3Field == null) ? null : /*isinst with value type is only supported in some contexts*/);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					return;
				}
				int num = Math.Max(0, val.GetLevel() - 1);
				if (num > 4)
				{
					num = 4;
				}
				if (num >= 1 && !IsWorldBoss(val))
				{
					if ((Object)(object)val3 != (Object)null)
					{
						((Component)val3).gameObject.SetActive(num >= 1);
					}
					if ((Object)(object)val4 != (Object)null)
					{
						((Component)val4).gameObject.SetActive(num >= 2);
					}
				}
				Transform val5 = (((Object)(object)val3 != (Object)null) ? ((Transform)val3).parent : val2.transform);
				HideNamedStars(val5);
				if ((Object)(object)val4 != (Object)null && (Object)(object)((Transform)val4).parent != (Object)(object)val5)
				{
					HideNamedStars(((Transform)val4).parent);
				}
				DestroyNamedStars((Transform)(object)val4);
				if (num > 2 && !((Object)(object)val3 == (Object)null) && !((Object)(object)val5 == (Object)null))
				{
					((Component)val3).gameObject.SetActive(false);
					if ((Object)(object)val4 != (Object)null)
					{
						((Component)val4).gameObject.SetActive(false);
					}
					DrawStarRow(val3, val4, val5, num);
				}
			}

			private static void DrawStarRow(RectTransform level2, RectTransform level3, Transform parent, int stars)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: 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_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				//IL_009f: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = (((Object)(object)level3 != (Object)null) ? level3.anchoredPosition : level2.anchoredPosition);
				for (int i = 0; i < stars; i++)
				{
					RectTransform orCreateStar = GetOrCreateStar(parent, i, level2);
					if (!((Object)(object)orCreateStar == (Object)null))
					{
						orCreateStar.anchorMin = level2.anchorMin;
						orCreateStar.anchorMax = level2.anchorMax;
						orCreateStar.pivot = level2.pivot;
						orCreateStar.sizeDelta = level2.sizeDelta;
						((Transform)orCreateStar).localScale = ((Transform)level2).localScale;
						((Transform)orCreateStar).localRotation = ((Transform)level2).localRotation;
						float num = val.x + ((float)i - (float)(stars - 1) * 0.5f) * 18f;
						orCreateStar.anchoredPosition = new Vector2(num, val.y);
						((Transform)orCreateStar).SetAsLastSibling();
						((Component)orCreateStar).gameObject.SetActive(true);
					}
				}
			}

			private static RectTransform GetOrCreateStar(Transform parent, int index, RectTransform template)
			{
				string text = "g3a3_star_" + index;
				Transform val = parent.Find(text);
				if ((Object)(object)val != (Object)null)
				{
					return (RectTransform)(object)((val is RectTransform) ? val : null);
				}
				GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false);
				((Object)obj).name = text;
				StripNestedExtras(obj.transform);
				return obj.GetComponent<RectTransform>();
			}

			private static void StripNestedExtras(Transform root)
			{
				for (int num = root.childCount - 1; num >= 0; num--)
				{
					Transform child = root.GetChild(num);
					if (((Object)child).name.StartsWith("g3a3_star_"))
					{
						Object.Destroy((Object)(object)((Component)child).gameObject);
					}
				}
			}

			private static List<RectTransform> CollectVanillaStars(RectTransform group)
			{
				List<RectTransform> list = new List<RectTransform>();
				if ((Object)(object)group == (Object)null)
				{
					return list;
				}
				Graphic[] componentsInChildren = ((Component)group).GetComponentsInChildren<Graphic>(true);
				foreach (Graphic val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)(object)group) && !IsExtraStar(((Component)val).transform, (Transform)(object)group))
					{
						RectTransform rectTransform = val.rectTransform;
						if ((Object)(object)rectTransform != (Object)null && !list.Contains(rectTransform))
						{
							list.Add(rectTransform);
						}
					}
				}
				return list;
			}

			private static bool IsExtraStar(Transform node, Transform root)
			{
				while ((Object)(object)node != (Object)null && (Object)(object)node != (Object)(object)root)
				{
					if (((Object)node).name.StartsWith("g3a3_star_"))
					{
						return true;
					}
					node = node.parent;
				}
				return false;
			}

			private static RectTransform FindStarGraphic(RectTransform root)
			{
				if ((Object)(object)root == (Object)null)
				{
					return null;
				}
				Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(true);
				foreach (Graphic val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)((Component)val).transform).name.StartsWith("g3a3_star_") && (Object)(object)((Component)val).transform != (Object)(object)root)
					{
						return val.rectTransform;
					}
				}
				if ((Object)(object)((Component)root).GetComponent<Graphic>() != (Object)null)
				{
					return root;
				}
				return null;
			}

			private static void DestroyNamedStars(Transform parent)
			{
				if ((Object)(object)parent == (Object)null)
				{
					return;
				}
				for (int num = parent.childCount - 1; num >= 0; num--)
				{
					Transform child = parent.GetChild(num);
					if ((Object)(object)child != (Object)null && ((Object)child).name.StartsWith("g3a3_star_"))
					{
						Object.Destroy((Object)(object)((Component)child).gameObject);
					}
				}
			}

			private static void HideNamedStars(Transform parent)
			{
				if ((Object)(object)parent == (Object)null)
				{
					return;
				}
				for (int i = 0; i < 4; i++)
				{
					Transform val = parent.Find("g3a3_star_" + i);
					if ((Object)(object)val != (Object)null)
					{
						((Component)val).gameObject.SetActive(false);
					}
				}
			}
		}

		public const string PluginGUID = "g3a3.g3a3";

		public const string PluginName = "G3A3";

		public const string PluginVersion = "1.7.0";

		public const float DefaultDaySeconds = 2700f;

		public const float DefaultNightSeconds = 900f;

		public const float DefaultRestedSkillXpMultiplier = 3f;

		public const int DefaultForgeUpgradeChance = 90;

		public const int DefaultForgeBreakChance = 10;

		public const float DefaultLevel4HealthMultiplier = 4.5f;

		public const float DefaultLevel5HealthMultiplier = 6f;

		public const float DefaultLevel4DamageMultiplier = 2.5f;

		public const float DefaultLevel5DamageMultiplier = 3f;

		public const float DefaultLevel4LootMultiplier = 6f;

		public const float DefaultLevel5LootMultiplier = 8f;

		public const float DefaultBallistaViewDistance = 28f;

		public const float DefaultMapExploreOnFootMultiplier = 2f;

		public const float DefaultMapExploreOnShipMultiplier = 4f;

		public const int MaxCreatureLevel = 5;

		internal static ConfigEntry<float> DayLengthSeconds;

		internal static ConfigEntry<float> NightLengthSeconds;

		internal static ConfigEntry<bool> NightStarsEnabled;

		internal static ConfigEntry<bool> NightStarsUpgradeExisting;

		internal static ConfigEntry<bool> NightStarsKeepWhenTamed;

		internal static ConfigEntry<int> NightChance0Star;

		internal static ConfigEntry<int> NightChance1Star;

		internal static ConfigEntry<int> NightChance2Star;

		internal static ConfigEntry<bool> ExtraLevelsEnabled;

		internal static ConfigEntry<int> NightChance3Star;

		internal static ConfigEntry<int> NightChance4Star;

		internal static ConfigEntry<int> DayChance0Star;

		internal static ConfigEntry<int> DayChance1Star;

		internal static ConfigEntry<int> DayChance2Star;

		internal static ConfigEntry<int> DayChance3Star;

		internal static ConfigEntry<int> DayChance4Star;

		internal static ConfigEntry<float> Level4HealthMultiplier;

		internal static ConfigEntry<float> Level5HealthMultiplier;

		internal static ConfigEntry<float> Level4DamageMultiplier;

		internal static ConfigEntry<float> Level5DamageMultiplier;

		internal static ConfigEntry<float> Level4LootMultiplier;

		internal static ConfigEntry<float> Level5LootMultiplier;

		internal static ConfigEntry<float> BallistaViewDistance;

		internal static ConfigEntry<bool> MapExploreEnabled;

		internal static ConfigEntry<float> MapExploreOnFootMultiplier;

		internal static ConfigEntry<float> MapExploreOnShipMultiplier;

		private const string ZdoOrigLevel = "g3a3_origLevel";

		private const string ZdoNightApplied = "g3a3_nightApplied";

		private const string ZdoStarPeriod = "g3a3_starPeriod";

		private const int StarPeriodNone = 0;

		private const int StarPeriodDay = 1;

		private const int StarPeriodNight = 2;

		private static readonly HashSet<string> WorldBossPrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Eikthyr", "gd_king", "Bonemass", "Dragon", "GoblinKing", "SeekerQueen", "TheQueen", "Fader" };

		private static bool _hasStarPeriodState;

		private static int _knownStarPeriod = -1;

		private static bool _forceStarReroll;

		internal static ConfigEntry<float> RestedSkillXpMultiplier;

		internal static ConfigEntry<bool> ForgeOverrideChances;

		internal static ConfigEntry<int> ForgeUpgradeChance;

		internal static ConfigEntry<int> ForgeBreakChance;

		internal static ConfigEntry<bool> ArcherStaffEnabled;

		internal static Plugin Instance;

		private Harmony _harmony;

		private const string ArcherStaffPrefab = "G3A3_StaffSkeletonArcher";

		private const string ArcherSkeletonPrefab = "G3A3_SkeletonArcher";

		private const string ArcherSpawnPrefab = "G3A3_StaffSkeletonArcher_spawn";

		private const string VanillaSpawnPrefab = "staff_skeleton_spawn";

		private static bool _archerStaffRegistered;

		private static readonly (string Token, string English, string Russian)[] ArcherTexts = new(string, string, string)[3]
		{
			("item_g3a3_staffarchers", "Dead Raiser of Archers", "Воскрешатель лучников"),
			("item_g3a3_staffarchers_desc", "Raises only skeleton archers, two per staff quality. One cast summons a pair. Summon limit is 2, 4, 6 or 8. Strength follows Blood magic, the same as the Dead Raiser.", "Призывает только скелетов-лучников, по два на каждое качество посоха. За одно применение появляется пара. Лимит: 2, 4, 6 или 8. Сила зависит от магии крови, как у обычного Воскрешателя."),
			("enemy_g3a3_skeletonarcher", "Skeleton archer", "Скелет-лучник")
		};

		private const float WakeDisplayFraction = 0.28f;

		internal static float DaySeconds => Math.Max(1f, DayLengthSeconds?.Value ?? 2700f);

		internal static float NightSeconds => Math.Max(0f, NightLengthSeconds?.Value ?? 900f);

		internal static long TotalSeconds => (long)Math.Max(1f, DaySeconds + NightSeconds);

		private void Awake()
		{
			//IL_089b: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a5: Expected O, but got Unknown
			Instance = this;
			DayLengthSeconds = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Time", "DayLengthSeconds", 2700f, "EN: How long daytime lasts, in real seconds. 2700 = 45 minutes. Vanilla is 1260 (21 minutes). RU: Длительность дня в реальных секундах. 2700 = 45 мин. Ваниль 1260 (21 мин).", true, (int?)1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 86400f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightLengthSeconds = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Time", "NightLengthSeconds", 900f, "EN: How long nighttime lasts, in real seconds. 900 = 15 minutes. Vanilla is 540 (9 minutes). RU: Длительность ночи в реальных секундах. 900 = 15 мин. Ваниль 540 (9 мин).", true, (int?)2, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 86400f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightStarsEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "NightStars", "Enabled", true, "EN: When true, creatures roll star levels from the day or night weight tables. Spawn counts stay vanilla. World bosses are skipped. RU: Если true, мобы получают уровни (звёзды) по дневной или ночной таблице. Спавн как в ванили. Мировых боссов не трогаем.", true, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightStarsUpgradeExisting = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "NightStars", "UpgradeExisting", true, "EN: When true, creatures already in the world also roll when day or night begins, or when their zone loads. Wild creatures re-roll on the new table. Tamed keep stars if KeepStarsWhenTamed is true. RU: Если true, уже живущие мобы тоже бросают уровень на рассвете, закате и при загрузке зоны. Дикие перебрасываются по новой таблице. Прирученные оставляют звёзды, если включено KeepStarsWhenTamed.", true, (int?)2, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightStarsKeepWhenTamed = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "NightStars", "KeepStarsWhenTamed", true, "EN: When true, taming at night makes night stars permanent (they no longer revert at dawn). RU: Если true, приручение ночью фиксирует звёзды: на рассвете они не сбрасываются.", true, (int?)6, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightChance0Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "Chance0Star", 50, "EN: Weight for 0-star (level 1) at night. Relative to the other Chance*Star weights. RU: Вес 0★ (уровень 1) ночью. Это вес относительно остальных Chance*Star.", true, (int?)3, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightChance1Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "Chance1Star", 25, "EN: Weight for 1-star (level 2) at night. RU: Вес 1★ (уровень 2) ночью.", true, (int?)4, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightChance2Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "Chance2Star", 25, "EN: Weight for 2-star (level 3) at night. Vanilla maximum. RU: Вес 2★ (уровень 3) ночью. Ванильный максимум.", true, (int?)5, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ExtraLevelsEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "NightStars", "ExtraLevelsEnabled", false, "EN: MASTER SWITCH for extra levels 4-5 (3 and 4 stars next to the name). Default OFF. When false, extra-level weights and HP/loot/damage keys are ignored. Creature body/scale is never changed. RU: ГЛАВНЫЙ ФЛАГ доп. уровней 4-5 (3★ и 4★ у имени). По умолчанию ВЫКЛ. Если false, веса 3/4 звезды и ключи HP/лут/урон игнорируются. Модель и размер моба не меняются.", true, (int?)7, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightChance3Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "Chance3Star", 0, "EN: Weight for 3-star (level 4). Ignored unless ExtraLevelsEnabled is true. RU: Вес 3★ (уровень 4). Игнорируется, пока ExtraLevelsEnabled = false.", true, (int?)8, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			NightChance4Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "Chance4Star", 0, "EN: Weight for 4-star (level 5, hard cap). Ignored unless ExtraLevelsEnabled is true. RU: Вес 4★ (уровень 5, потолок). Игнорируется, пока ExtraLevelsEnabled = false.", true, (int?)9, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayChance0Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "DayChance0Star", 70, "EN: Daytime weight for 0-star (level 1). Separate from night weights. RU: Дневной вес 0★ (уровень 1). Отдельно от ночи.", true, (int?)16, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayChance1Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "DayChance1Star", 15, "EN: Daytime weight for 1-star (level 2). RU: Дневной вес 1★ (уровень 2).", true, (int?)17, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayChance2Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "DayChance2Star", 5, "EN: Daytime weight for 2-star (level 3). RU: Дневной вес 2★ (уровень 3).", true, (int?)18, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayChance3Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "DayChance3Star", 5, "EN: Daytime weight for 3-star (level 4). Ignored unless ExtraLevelsEnabled is true. RU: Дневной вес 3★ (уровень 4). Игнорируется, пока ExtraLevelsEnabled = false.", true, (int?)19, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayChance4Star = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "NightStars", "DayChance4Star", 5, "EN: Daytime weight for 4-star (level 5). Ignored unless ExtraLevelsEnabled is true. RU: Дневной вес 4★ (уровень 5). Игнорируется, пока ExtraLevelsEnabled = false.", true, (int?)20, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level4HealthMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level4HealthMultiplier", 4.5f, "EN: Max health as a multiple of 0-star base HP at level 4. Vanilla would be 4.0. 4.5 = +150% base on top of level 3. RU: Макс. HP на 4 ур. как множитель базы 0★. Ваниль была бы 4.0. 4.5 = +150% базы поверх уровня 3.", true, (int?)10, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level5HealthMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level5HealthMultiplier", 6f, "EN: Max health multiple of 0-star base HP at level 5. Vanilla would be 5.0. 6.0 = another +150% base. RU: Макс. HP на 5 ур. как множитель базы 0★. Ваниль была бы 5.0. 6.0 = ещё +150% базы.", true, (int?)11, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level4DamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level4DamageMultiplier", 2.5f, "EN: Damage multiple at level 4. Vanilla formula is 2.5. Leave 2.5 to keep attack as-is. RU: Множитель урона на 4 ур. Ваниль 2.5. Оставьте 2.5, чтобы атака не менялась.", true, (int?)12, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level5DamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level5DamageMultiplier", 3f, "EN: Damage multiple at level 5. Vanilla formula is 3.0. RU: Множитель урона на 5 ур. Ваниль 3.0.", true, (int?)13, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level4LootMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level4LootMultiplier", 6f, "EN: Loot amount/chance multiplier at level 4. Vanilla would be 8. Trophy count stays 1. RU: Множитель количества/шанса лута на 4 ур. Ваниль была бы 8. Трофей по-прежнему 1 шт.", true, (int?)14, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 64f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			Level5LootMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "NightStars", "Level5LootMultiplier", 8f, "EN: Loot multiplier at level 5. Vanilla would be 16. Trophy count stays 1. RU: Множитель лута на 5 ур. Ваниль была бы 16. Трофей по-прежнему 1 шт.", true, (int?)15, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 64f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			RestedSkillXpMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Rested", "SkillXpMultiplier", 3f, "EN: Skill XP multiplier while Rested. Vanilla is 1.5 (+50%). 3.0 = triple XP. Only while the buff is active. RU: Множитель опыта навыков при бодрости. Ваниль 1.5 (+50%). 3.0 = тройной опыт. Только пока бафф активен.", true, (int?)1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ForgeOverrideChances = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "ForgeOfPotential", "OverrideChances", true, "EN: When true, overrides Forge of Potential upgrade/break chances for all items. RU: Если true, подменяет шансы улучшения/поломки кузницы возможностей для всех предметов.", true, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ForgeUpgradeChance = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "ForgeOfPotential", "UpgradeChance", 90, "EN: Percent chance a Forge of Potential refinement succeeds. Vanilla is 65. RU: Шанс успешного улучшения, %. Ваниль 65.", true, (int?)2, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ForgeBreakChance = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "ForgeOfPotential", "BreakChance", 10, "EN: Percent chance a failed roll breaks the item. Vanilla is 10. With UpgradeChance 90 and BreakChance 10 the result is 90% success / 10% break. RU: Шанс поломки при неудаче, %. Ваниль 10. При 90/10 получается 90% успех / 10% поломка.", true, (int?)3, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ArcherStaffEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "ArcherStaff", "Enabled", false, "EN: When true, adds Dead Raiser of Archers. Galdr craft and upgrade costs are double the Dead Raiser. Summons only skeleton archers, two per staff quality (2, 4, 6, 8). One cast summons a pair. Forge of Potential still takes one idol. Off by default. Restart after turning it off. RU: Если true, добавляет Воскрешатель лучников. Крафт и улучшение на столе гальдров стоят вдвое дороже обычного Воскрешателя. Только скелеты-лучники, по два на качество посоха (2, 4, 6, 8). За одно применение появляется пара. В кузнице возможностей по-прежнему один идол. По умолчанию выключено. После выключения нужен перезапуск.", true, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BallistaViewDistance = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Ballista", "ViewDistance", 28f, "EN: Ballista target acquisition range in meters. Vanilla is 28. Any positive value works. RU: Дальность поиска цели баллисты в метрах. Ваниль 28. Любое положительное значение.", true, (int?)1, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 500f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MapExploreEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "MapExplore", "Enabled", true, "EN: When true, multiplies map fog reveal radius on foot and on ships. Turn off if another map-radius mod is used. RU: Если true, множит радиус раскрытия тумана карты пешком и на корабле. Выключите, если стоит другой мод на радиус карты.", true, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MapExploreOnFootMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "MapExplore", "OnFootMultiplier", 2f, "EN: Map explore radius multiplier while on foot. 2 = twice vanilla radius. RU: Множитель радиуса раскрытия карты пешком. 2 = вдвое больше ванили.", true, (int?)2, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 50f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MapExploreOnShipMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "MapExplore", "OnShipMultiplier", 4f, "EN: Map explore radius multiplier while on a ship (helm or passenger). 4 = four times vanilla radius. RU: Множитель радиуса раскрытия карты на корабле (штурвал или пассажир). 4 = вчетверо ванили.", true, (int?)3, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 50f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DayLengthSeconds.SettingChanged += delegate
			{
				ApplyToEnvMan();
			};
			NightLengthSeconds.SettingChanged += delegate
			{
				ApplyToEnvMan();
			};
			RestedSkillXpMultiplier.SettingChanged += delegate
			{
				ApplyRestedXpSettings();
			};
			NightStarsEnabled.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			NightStarsUpgradeExisting.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			ExtraLevelsEnabled.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			DayChance0Star.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			DayChance1Star.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			DayChance2Star.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			DayChance3Star.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			DayChance4Star.SettingChanged += delegate
			{
				InvalidateNightStarState(forceReroll: true);
			};
			BallistaViewDistance.SettingChanged += delegate
			{
				ApplyBallistaViewDistanceToLoaded();
			};
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				ApplyToEnvMan();
				ApplyRestedXpSettings();
				InvalidateNightStarState(forceReroll: true);
				RegisterArcherStaff();
				ApplyBallistaViewDistanceToLoaded();
			};
			ArcherStaffEnabled.SettingChanged += delegate
			{
				RegisterArcherStaff();
			};
			PrefabManager.OnVanillaPrefabsAvailable += RegisterArcherStaff;
			ItemManager.OnItemsRegistered += EnsureArcherForgeRecipe;
			AddArcherStaffLocalization();
			_harmony = new Harmony("g3a3.g3a3");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Day={2}s Night={3}s NightStars={4} extraLevels={5} upgradeExisting={6} keepWhenTamed={7} night=({8}/{9}/{10}/{11}/{12}) day=({13}/{14}/{15}/{16}/{17}) RestedXp={18} Forge={19}/{20}", "G3A3", "1.7.0", DayLengthSeconds.Value, NightLengthSeconds.Value, NightStarsEnabled.Value, ExtraLevelsEnabled.Value, NightStarsUpgradeExisting.Value, NightStarsKeepWhenTamed.Value, NightChance0Star.Value, NightChance1Star.Value, NightChance2Star.Value, NightChance3Star.Value, NightChance4Star.Value, DayChance0Star.Value, DayChance1Star.Value, DayChance2Star.Value, DayChance3Star.Value, DayChance4Star.Value, RestedSkillXpMultiplier.Value, ForgeUpgradeChance.Value, ForgeBreakChance.Value));
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterArcherStaff;
			ItemManager.OnItemsRegistered -= EnsureArcherForgeRecipe;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void RegisterArcherStaff()
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			if (_archerStaffRegistered || ArcherStaffEnabled == null || !ArcherStaffEnabled.Value || (Object)(object)PrefabManager.Instance.GetPrefab("StaffSkeleton") == (Object)null || (Object)(object)PrefabManager.Instance.GetPrefab("Skeleton_Friendly") == (Object)null || (Object)(object)PrefabManager.Instance.GetPrefab("staff_skeleton_spawn") == (Object)null)
			{
				return;
			}
			try
			{
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("G3A3_SkeletonArcher", "Skeleton_Friendly");
				if ((Object)(object)val == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: prefab Skeleton_Friendly was not found.");
				}
				else
				{
					if (!KeepOnlyArcherWeapons(val))
					{
						return;
					}
					GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("G3A3_StaffSkeletonArcher_spawn", "staff_skeleton_spawn");
					SpawnAbility val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<SpawnAbility>() : null);
					if ((Object)(object)val3 == (Object)null)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: prefab staff_skeleton_spawn has no SpawnAbility.");
						return;
					}
					val3.m_spawnPrefab = (GameObject[])(object)new GameObject[1] { val };
					val3.m_minToSpawn = 2;
					val3.m_maxToSpawn = 2;
					PrefabManager.Instance.AddPrefab(val);
					PrefabManager.Instance.AddPrefab(val2);
					ItemConfig val4 = new ItemConfig
					{
						Name = "$item_g3a3_staffarchers",
						Description = "$item_g3a3_staffarchers_desc",
						CraftingStation = CraftingStations.GaldrTable,
						RepairStation = CraftingStations.GaldrTable,
						MinStationLevel = 1
					};
					CopyDeadRaiserRecipe(val4);
					CustomItem val5 = new CustomItem("G3A3_StaffSkeletonArcher", "StaffSkeleton", val4);
					ItemDrop itemDrop = val5.ItemDrop;
					if (itemDrop?.m_itemData?.m_shared != null)
					{
						itemDrop.m_itemData.m_shared.m_name = "$item_g3a3_staffarchers";
						itemDrop.m_itemData.m_shared.m_description = "$item_g3a3_staffarchers_desc";
						if (!RetargetSummon(itemDrop.m_itemData.m_shared.m_attack, val2) && !RetargetSummon(itemDrop.m_itemData.m_shared.m_secondaryAttack, val2))
						{
							((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: Dead Raiser attack has no projectile to retarget.");
						}
					}
					if (!ItemManager.Instance.AddItem(val5))
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: failed to register G3A3_StaffSkeletonArcher");
						return;
					}
					AddArcherStaffLocalization();
					_archerStaffRegistered = true;
					EnsureArcherForgeRecipe();
					((BaseUnityPlugin)this).Logger.LogInfo((object)"G3A3: archer Dead Raiser registered as G3A3_StaffSkeletonArcher");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("G3A3: archer Dead Raiser failed. " + ex));
			}
		}

		private bool KeepOnlyArcherWeapons(GameObject skeleton)
		{
			Humanoid component = skeleton.GetComponent<Humanoid>();
			if ((Object)(object)component == (Object)null || component.m_randomWeapon == null || component.m_randomWeapon.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: Skeleton_Friendly has no random weapons.");
				return false;
			}
			List<GameObject> list = new List<GameObject>();
			GameObject[] randomWeapon = component.m_randomWeapon;
			foreach (GameObject val in randomWeapon)
			{
				if (!((Object)(object)val == (Object)null))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("G3A3: Skeleton_Friendly weapon option " + ((Object)val).name));
					if (((Object)val).name.IndexOf("bow", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						list.Add(val);
					}
				}
			}
			if (list.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"G3A3: Skeleton_Friendly weapon list has no bow.");
				return false;
			}
			component.m_defaultItems = Array.Empty<GameObject>();
			component.m_randomWeapon = list.ToArray();
			component.m_randomShield = Array.Empty<GameObject>();
			Character component2 = skeleton.GetComponent<Character>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_name = "$enemy_g3a3_skeletonarcher";
			}
			return true;
		}

		private static bool RetargetSummon(Attack attack, GameObject spawner)
		{
			if (attack == null)
			{
				return false;
			}
			bool result = false;
			if ((Object)(object)attack.m_attackProjectile != (Object)null)
			{
				attack.m_attackProjectile = spawner;
				result = true;
			}
			if ((Object)(object)attack.m_spawnOnTrigger != (Object)null && ((Object)attack.m_spawnOnTrigger).name.IndexOf("staff_skeleton_spawn", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				attack.m_spawnOnTrigger = spawner;
				result = true;
			}
			return result;
		}

		private void CopyDeadRaiserRecipe(ItemConfig config)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab("StaffSkeleton");
			ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null);
			Recipe val2 = (((Object)(object)val != (Object)null && (Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetRecipe(val.m_itemData) : null);
			if (val2?.m_resources != null && val2.m_resources.Length != 0)
			{
				if ((Object)(object)val2.m_craftingStation != (Object)null)
				{
					config.CraftingStation = ((Object)val2.m_craftingStation).name;
				}
				config.RepairStation = (((Object)(object)val2.m_repairStation != (Object)null) ? ((Object)val2.m_repairStation).name : config.CraftingStation);
				config.MinStationLevel = val2.m_minStationLevel;
				Requirement[] resources = val2.m_resources;
				foreach (Requirement val3 in resources)
				{
					if (!((Object)(object)val3?.m_resItem == (Object)null) && val3.m_amount >= 1)
					{
						bool upgraderResource = val3.m_upgraderResource;
						int num = (upgraderResource ? val3.m_amount : (val3.m_amount * 2));
						int num2 = (upgraderResource ? val3.m_amountPerLevel : (val3.m_amountPerLevel * 2));
						config.AddRequirement(((Object)val3.m_resItem).name, num, num2);
					}
				}
			}
			else
			{
				config.AddRequirement("BoneFragments", 20, 10);
				config.AddRequirement("Eitr", 32, 16);
				config.AddRequirement("TrophySkeleton", 8, 4);
				config.AddRequirement("Upgrader5Weapon", 1, 0);
			}
		}

		private void EnsureArcherForgeRecipe()
		{
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			if (!_archerStaffRegistered || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("StaffSkeleton");
			GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("G3A3_StaffSkeletonArcher");
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			ItemDrop val2 = (((Object)(object)itemPrefab2 != (Object)null) ? itemPrefab2.GetComponent<ItemDrop>() : null);
			if (val?.m_itemData?.m_shared == null || val2?.m_itemData?.m_shared == null)
			{
				return;
			}
			val2.m_itemData.m_shared.m_upgradeChance = val.m_itemData.m_shared.m_upgradeChance;
			val2.m_itemData.m_shared.m_breakChance = val.m_itemData.m_shared.m_breakChance;
			Recipe recipe = ObjectDB.instance.GetRecipe(val.m_itemData);
			Recipe recipe2 = ObjectDB.instance.GetRecipe(val2.m_itemData);
			if (recipe?.m_resources == null || (Object)(object)recipe2 == (Object)null)
			{
				return;
			}
			Requirement[] array = (Requirement[])(object)new Requirement[recipe.m_resources.Length];
			for (int i = 0; i < recipe.m_resources.Length; i++)
			{
				Requirement val3 = recipe.m_resources[i];
				bool flag = val3?.m_upgraderResource ?? false;
				int num = val3?.m_amount ?? 1;
				int num2 = val3?.m_amountPerLevel ?? 0;
				if (!flag)
				{
					num *= 2;
					num2 *= 2;
				}
				array[i] = new Requirement
				{
					m_resItem = val3?.m_resItem,
					m_amount = num,
					m_amountPerLevel = num2,
					m_recover = (val3?.m_recover ?? false),
					m_upgraderResource = flag
				};
			}
			recipe2.m_resources = array;
			if ((Object)(object)recipe.m_craftingStation != (Object)null)
			{
				recipe2.m_craftingStation = recipe.m_craftingStation;
			}
			if ((Object)(object)recipe.m_repairStation != (Object)null)
			{
				recipe2.m_repairStation = recipe.m_repairStation;
			}
			recipe2.m_minStationLevel = recipe.m_minStationLevel;
		}

		private static void AddArcherStaffLocalization()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = PlayerPrefs.GetString("language", "English");
			(string, string, string)[] archerTexts = ArcherTexts;
			for (int i = 0; i < archerTexts.Length; i++)
			{
				(string, string, string) tuple = archerTexts[i];
				string item = tuple.Item1;
				string item2 = tuple.Item2;
				string item3 = tuple.Item3;
				string text2 = "English";
				localization.AddTranslation(ref text2, ref item, item2);
				text2 = "Russian";
				localization.AddTranslation(ref text2, ref item, item3);
				if (Localization.instance != null)
				{
					AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null)?.Invoke(Localization.instance, new object[2]
					{
						item,
						(text == "Russian") ? item3 : item2
					});
				}
			}
		}

		internal static float MorningRawFraction()
		{
			float daySeconds = DaySeconds;
			float nightSeconds = NightSeconds;
			float num = Math.Max(1f, daySeconds + nightSeconds);
			if (nightSeconds <= 0.01f)
			{
				return 0.5f;
			}
			float num2 = nightSeconds / num * 0.5f;
			float val = daySeconds / num;
			float num3 = 0.060000002f;
			return Mathf.Clamp01(num2 + num3 * Math.Max(0.0001f, val));
		}

		internal static float RescaleRawFraction(float fraction)
		{
			float daySeconds = DaySeconds;
			float nightSeconds = NightSeconds;
			float num = Math.Max(1f, daySeconds + nightSeconds);
			float num2 = daySeconds / num;
			float num3 = nightSeconds / num * 0.5f;
			if (nightSeconds <= 0.01f)
			{
				return 0.5f;
			}
			if (num2 <= 0.01f)
			{
				if (!(fraction < 0.5f))
				{
					return 1f;
				}
				return 0f;
			}
			float num4 = num3 + num2;
			if (fraction >= num3 && fraction <= num4)
			{
				float num5 = (fraction - num3) / num2;
				return 0.25f + num5 * 0.5f;
			}
			if (fraction < 0.5f)
			{
				return fraction / Math.Max(0.0001f, num3) * 0.25f;
			}
			float num6 = (fraction - num4) / Math.Max(0.0001f, num3);
			return 0.75f + num6 * 0.25f;
		}

		internal static long CycleSeconds(EnvMan env = null)
		{
			env = env ?? EnvMan.instance;
			if ((Object)(object)env != (Object)null && env.m_dayLengthSec > 0)
			{
				return env.m_dayLengthSec;
			}
			return TotalSeconds;
		}

		internal static float GetRawDayFraction(EnvMan env = null)
		{
			env = env ?? EnvMan.instance;
			if ((Object)(object)env == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return 0.5f;
			}
			long num = CycleSeconds(env);
			double num2 = (double)num * 1000.0;
			if (num2 <= 0.0)
			{
				return 0.5f;
			}
			double num3 = ZNet.instance.GetTimeSeconds() * 1000.0 % num2;
			if (num3 < 0.0)
			{
				num3 += num2;
			}
			return Mathf.Clamp01((float)(num3 / 1000.0 / (double)num));
		}

		internal static float GetTrueDisplayFraction(EnvMan env = null)
		{
			return RescaleRawFraction(GetRawDayFraction(env));
		}

		internal static void SnapSmoothDayFraction(EnvMan env)
		{
			if (!((Object)(object)env == (Object)null))
			{
				AccessTools.Field(typeof(EnvMan), "m_smoothDayFraction")?.SetValue(env, GetTrueDisplayFraction(env));
			}
		}

		internal static void ApplyToEnvMan()
		{
			if (!((Object)(object)EnvMan.instance == (Object)null))
			{
				EnvMan.instance.m_dayLengthSec = TotalSeconds;
			}
		}

		internal static float RestedSkillXpModifier()
		{
			return Math.Max(0f, RestedSkillXpMultiplier?.Value ?? 3f) - 1f;
		}

		internal static void ApplyRestedXpModifier(StatusEffect statusEffect)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			SE_Stats val = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null);
			if (val != null)
			{
				val.m_raiseSkill = (SkillType)999;
				val.m_raiseSkillModifier = RestedSkillXpModifier();
			}
		}

		internal static void ApplyRestedXpSettings()
		{
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				ApplyRestedXpModifier(ObjectDB.instance.GetStatusEffect(SEMan.s_statusEffectRested));
			}
			List<Player> allPlayers = Player.GetAllPlayers();
			if (allPlayers == null)
			{
				return;
			}
			foreach (Player item in allPlayers)
			{
				if (!((Object)(object)item == (Object)null))
				{
					SEMan sEMan = ((Character)item).GetSEMan();
					ApplyRestedXpModifier((sEMan != null) ? sEMan.GetStatusEffect(SEMan.s_statusEffectRested) : null);
				}
			}
		}

		internal static void ApplyBallistaViewDistance(Turret turret)
		{
			if (!((Object)(object)turret == (Object)null) && BallistaViewDistance != null)
			{
				turret.m_viewDistance = Math.Max(1f, BallistaViewDistance.Value);
			}
		}

		internal static void ApplyBallistaViewDistanceToLoaded()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Turret[] array = Resources.FindObjectsOfTypeAll<Turret>();
			if (array == null)
			{
				return;
			}
			Turret[] array2 = array;
			foreach (Turret val in array2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						ApplyBallistaViewDistance(val);
					}
				}
			}
		}

		internal static bool IsLocalPlayerOnShip()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			if (!((Character)localPlayer).IsAttachedToShip() && !((Object)(object)localPlayer.GetControlledShip() != (Object)null) && !((Object)(object)Ship.GetLocalShip() != (Object)null))
			{
				return (Object)(object)((Component)localPlayer).GetComponentInParent<Ship>() != (Object)null;
			}
			return true;
		}

		internal static float GetMapExploreMultiplier()
		{
			if (MapExploreEnabled == null || !MapExploreEnabled.Value)
			{
				return 1f;
			}
			if (IsLocalPlayerOnShip())
			{
				return Math.Max(0.1f, MapExploreOnShipMultiplier?.Value ?? 4f);
			}
			return Math.Max(0.1f, MapExploreOnFootMultiplier?.Value ?? 2f);
		}

		internal static bool StarsEnabled()
		{
			if (NightStarsEnabled != null)
			{
				return NightStarsEnabled.Value;
			}
			return false;
		}

		internal static bool ShouldApplyNightStars()
		{
			return StarsEnabled();
		}

		internal static int CurrentStarPeriod()
		{
			if (!StarsEnabled())
			{
				return 0;
			}
			if (!EnvMan.IsNight())
			{
				return 1;
			}
			return 2;
		}

		internal static bool ExtraLevelsOn()
		{
			if (ExtraLevelsEnabled != null)
			{
				return ExtraLevelsEnabled.Value;
			}
			return false;
		}

		internal static int RollNightLevel()
		{
			return RollStarLevel(night: true);
		}

		internal static int RollDayLevel()
		{
			return RollStarLevel(night: false);
		}

		internal static int RollStarLevel(bool night)
		{
			int num = Math.Max(0, (!night) ? (DayChance0Star?.Value ?? 70) : (NightChance0Star?.Value ?? 50));
			int num2 = Math.Max(0, (!night) ? (DayChance1Star?.Value ?? 15) : (NightChance1Star?.Value ?? 25));
			int num3 = Math.Max(0, (!night) ? (DayChance2Star?.Value ?? 5) : (NightChance2Star?.Value ?? 25));
			int num4 = (ExtraLevelsOn() ? Math.Max(0, (!night) ? (DayChance3Star?.Value ?? 5) : (NightChance3Star?.Value ?? 0)) : 0);
			int num5 = (ExtraLevelsOn() ? Math.Max(0, (!night) ? (DayChance4Star?.Value ?? 5) : (NightChance4Star?.Value ?? 0)) : 0);
			int num6 = num + num2 + num3 + num4 + num5;
			if (num6 <= 0)
			{
				return 1;
			}
			float num7 = Random.Range(0f, (float)num6);
			if (num7 < (float)num)
			{
				return 1;
			}
			if (num7 < (float)(num + num2))
			{
				return 2;
			}
			if (num7 < (float)(num + num2 + num3))
			{
				return 3;
			}
			if (num7 < (float)(num + num2 + num3 + num4))
			{
				return 4;
			}
			return 5;
		}

		internal static float GetExtraHealthMultiplier(int level)
		{
			if (!ExtraLevelsOn() || level < 4)
			{
				return -1f;
			}
			if (level == 4)
			{
				return Math.Max(1f, Level4HealthMultiplier?.Value ?? 4.5f);
			}
			return Math.Max(1f, Level5HealthMultiplier?.Value ?? 6f);
		}

		internal static bool TryGetExtraDamageMultiplier(int level, out float multiplier)
		{
			multiplier = 0f;
			if (!ExtraLevelsOn() || level < 4)
			{
				return false;
			}
			multiplier = ((level == 4) ? Math.Max(0.1f, Level4DamageMultiplier?.Value ?? 2.5f) : Math.Max(0.1f, Level5DamageMultiplier?.Value ?? 3f));
			return true;
		}

		public static int OverrideLootMultiplier(Character character, int vanilla)
		{
			if ((Object)(object)character == (Object)null || !ExtraLevelsOn())
			{
				return vanilla;
			}
			int level = character.GetLevel();
			if (level == 4)
			{
				return Math.Max(0, Mathf.RoundToInt(Level4LootMultiplier?.Value ?? 6f));
			}
			if (level >= 5)
			{
				return Math.Max(0, Mathf.RoundToInt(Level5LootMultiplier?.Value ?? 8f));
			}
			return vanilla;
		}

		internal static void InvalidateNightStarState(bool forceReroll = false)
		{
			_hasStarPeriodState = false;
			_knownStarPeriod = -1;
			if (forceReroll)
			{
				_forceStarReroll = true;
			}
			RefreshLoadedCreatureStars();
		}

		internal static int DesiredStarPeriod()
		{
			return CurrentStarPeriod();
		}

		internal static int GetStoredStarPeriod(ZDO zdo)
		{
			if (zdo == null)
			{
				return 0;
			}
			int num = zdo.GetInt("g3a3_starPeriod", 0);
			if (num == 0 && zdo.GetBool("g3a3_nightApplied", false))
			{
				return 2;
			}
			return num;
		}

		internal static void SetStoredStarPeriod(ZDO zdo, int period)
		{
			zdo.Set("g3a3_starPeriod", period);
			zdo.Set("g3a3_nightApplied", period == 2);
		}

		internal static void RefreshLoadedCreatureStars()
		{
			if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			int num = DesiredStarPeriod();
			bool forceStarReroll = _forceStarReroll;
			if (_hasStarPeriodState && !forceStarReroll && num == _knownStarPeriod)
			{
				return;
			}
			_hasStarPeriodState = true;
			_knownStarPeriod = num;
			_forceStarReroll = false;
			List<Character> allCharacters = Character.GetAllCharacters();
			if (allCharacters == null)
			{
				return;
			}
			int num2 = 0;
			foreach (Character item in allCharacters)
			{
				if (num == 0)
				{
					if (RevertNightStars(item))
					{
						num2++;
					}
				}
				else if (ApplyNightStars(item, newlySpawned: false, forceStarReroll))
				{
					num2++;
				}
			}
			if (num2 > 0)
			{
				string arg = num switch
				{
					1 => "Day", 
					2 => "Night", 
					_ => "Off", 
				};
				Plugin instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)$"{arg} stars applied to {num2} loaded creatures");
				}
			}
		}

		internal static void SyncCreatureNightStars(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)EnvMan.instance == (Object)null)
			{
				return;
			}
			if (StarsEnabled())
			{
				bool flag = true;
				if (TryGetOwnedZdo(character, out var zdo))
				{
					flag = GetStoredStarPeriod(zdo) == 0;
				}
				if (!ApplyNightStars(character, flag || NightStarsUpgradeExisting == null || NightStarsUpgradeExisting.Value) && StarsEnabled() && IsNightStarCandidate(character) && (Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StartCoroutine(RetryNightStars(character));
				}
			}
			else
			{
				RevertNightStars(character);
			}
		}

		internal static IEnumerator RetryNightStars(Character character)
		{
			for (int i = 0; i < 8; i++)
			{
				yield return (object)new WaitForSeconds(0.25f);
				if ((Object)(object)character == (Object)null || !StarsEnabled() || !IsNightStarCandidate(character) || ApplyNightStars(character, newlySpawned: true) || (TryGetOwnedZdo(character, out var zdo) && GetStoredStarPeriod(zdo) == DesiredStarPeriod()))
				{
					break;
				}
			}
		}

		internal static bool IsNightStarCandidate(Character character)
		{
			if ((Object)(object)character == (Object)null || character.IsPlayer() || character.IsTamed() || IsWorldBoss(character))
			{
				return false;
			}
			return (Object)(object)((Component)character).GetComponent<Fish>() == (Object)null;
		}

		internal static bool IsWorldBoss(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)((Component)character).gameObject == (Object)null)
			{
				return false;
			}
			return WorldBossPrefabs.Contains(GetPrefabName(((Component)character).gameObject));
		}

		internal static string GetPrefabName(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return string.Empty;
			}
			string text = ((Object)go).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		internal static Character FindCharacter(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return null;
			}
			Character component = go.GetComponent<Character>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			return go.GetComponentInChildren<Character>(true);
		}

		internal static bool TryGetOwnedZdo(Character character, out ZDO zdo)
		{
			zdo = null;
			ZNetView component = ((Component)character).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
			{
				return false;
			}
			zdo = component.GetZDO();
			return zdo != null;
		}

		public static void ApplyNightStarsFromObject(GameObject go)
		{
			ApplyNightStars(FindCharacter(go), newlySpawned: true);
		}

		public static void ApplyNightStarsFromObject(GameObject go, bool eventSpawner)
		{
			ApplyNightStarsFromObject(go);
		}

		public static void ApplyNightStars(Character character)
		{
			ApplyNightStars(character, newlySpawned: true);
		}

		public static bool ApplyNightStars(Character character, bool newlySpawned)
		{
			return ApplyNightStars(character, newlySpawned, forceReroll: false);
		}

		public static bool ApplyNightStars(Character character, bool newlySpawned, bool forceReroll)
		{
			if (!StarsEnabled() || (Object)(object)character == (Object)null)
			{
				return false;
			}
			if (!newlySpawned && (NightStarsUpgradeExisting == null || !NightStarsUpgradeExisting.Value))
			{
				return false;
			}
			if (!IsNightStarCandidate(character) || !TryGetOwnedZdo(character, out var zdo))
			{
				return false;
			}
			int num = DesiredStarPeriod();
			if (num == 0)
			{
				return false;
			}
			int storedStarPeriod = GetStoredStarPeriod(zdo);
			if (!forceReroll && storedStarPeriod == num)
			{
				return false;
			}
			int num2 = Math.Max(1, character.GetLevel());
			int num3 = zdo.GetInt("g3a3_origLevel", 0);
			if (num3 < 1)
			{
				num3 = num2;
				zdo.Set("g3a3_origLevel", num3);
			}
			int val = ((num == 2) ? RollNightLevel() : RollDayLevel());
			character.SetLevel(Math.Min(5, Math.Max(1, val)));
			SetStoredStarPeriod(zdo, num);
			return true;
		}

		public static bool RevertNightStars(Character character)
		{
			if ((Object)(object)character == (Object)null || character.IsPlayer() || !TryGetOwnedZdo(character, out var zdo))
			{
				return false;
			}
			if (GetStoredStarPeriod(zdo) == 0 && zdo.GetInt("g3a3_origLevel", 0) < 1)
			{
				return false;
			}
			if (character.IsTamed() && NightStarsKeepWhenTamed != null && NightStarsKeepWhenTamed.Value)
			{
				KeepTamedNightStars(character);
				return false;
			}
			int level = Math.Max(1, zdo.GetInt("g3a3_origLevel", 1));
			SetStoredStarPeriod(zdo, 0);
			character.SetLevel(level);
			return true;
		}

		public static void KeepTamedNightStars(Character character)
		{
			if (!((Object)(object)character == (Object)null) && NightStarsKeepWhenTamed != null && NightStarsKeepWhenTamed.Value && character.IsTamed() && TryGetOwnedZdo(character, out var zdo) && GetStoredStarPeriod(zdo) != 0)
			{
				zdo.Set("g3a3_origLevel", Math.Max(1, character.GetLevel()));
				SetStoredStarPeriod(zdo, 0);
			}
		}

		internal static float OverrideUpgradeChance(float vanilla)
		{
			if (ForgeOverrideChances == null || !ForgeOverrideChances.Value)
			{
				return vanilla;
			}
			return Mathf.Clamp01((float)(ForgeUpgradeChance?.Value ?? 90) / 100f);
		}

		internal static float OverrideBreakChance(float vanilla)
		{
			if (ForgeOverrideChances == null || !ForgeOverrideChances.Value)
			{
				return vanilla;
			}
			return Mathf.Clamp01((float)(ForgeBreakChance?.Value ?? 10) / 100f);
		}
	}
}