Decompiled source of LethalSnapRewardsAddon v1.0.0

plugins/LethalSnapRewardsAddon/LethalSnapRewards.Preview.dll

Decompiled 25 minutes ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Wesley.LethalSnapRewards;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("local.wesley.lethalsnap.rewards.preview", "Lethal Snap Rewards Addon", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class RewardsPreviewPlugin : BaseUnityPlugin
{
	private struct GhostPhoto
	{
		public int PhotoId;

		public ulong CameraId;

		public ulong GhostId;

		public float ReceivedAt;
	}

	private sealed class PhotoShot
	{
		public long Epoch;

		public PlayerControllerB Shooter;

		public bool StartedInExpedition;

		public PhotoReward Reward;

		public Payout Payout;
	}

	public const string PluginId = "local.wesley.lethalsnap.rewards.preview";

	public const string PluginVersion = "1.0.0";

	public const string EmotePluginId = "com.seeya.muchbetteremotes";

	public const string BrackenPluginId = "Ovchinikov.SnatchinBracken.Main";

	private const string GhostPhotoMessage = "wesley.lethalsnap.rewards.ghost-photo.v1";

	private static ManualLogSource log;

	private static PhotoRewardCalculator calculator;

	private static RewardSettings settings;

	private static readonly Dictionary<Type, string> VanillaPriceNames = new Dictionary<Type, string>
	{
		{
			typeof(SandSpiderAI),
			"Bunker Spider"
		},
		{
			typeof(CentipedeAI),
			"Centipede"
		},
		{
			typeof(HoarderBugAI),
			"Hoarding Bug"
		},
		{
			typeof(CrawlerAI),
			"Crawler"
		},
		{
			typeof(PufferAI),
			"Puffer"
		},
		{
			typeof(FlowermanAI),
			"Flowerman"
		},
		{
			typeof(SpringManAI),
			"Spring"
		},
		{
			typeof(DressGirlAI),
			"Girl"
		},
		{
			typeof(JesterAI),
			"Jester"
		},
		{
			typeof(NutcrackerEnemyAI),
			"Nutcracker"
		},
		{
			typeof(MaskedPlayerEnemy),
			"Masked"
		},
		{
			typeof(ButlerEnemyAI),
			"Butler"
		},
		{
			typeof(ButlerBeesEnemyAI),
			"Butler Bees"
		},
		{
			typeof(BlobAI),
			"Blob"
		},
		{
			typeof(CaveDwellerAI),
			"Maneater"
		},
		{
			typeof(ClaySurgeonAI),
			"Clay Surgeon"
		},
		{
			typeof(MouthDogAI),
			"MouthDog"
		},
		{
			typeof(ForestGiantAI),
			"ForestGiant"
		},
		{
			typeof(SandWormAI),
			"Earth Leviathan"
		},
		{
			typeof(BaboonBirdAI),
			"Baboon Hawk"
		},
		{
			typeof(FlowerSnakeEnemy),
			"Tulip Snake"
		},
		{
			typeof(RedLocustBees),
			"Red Locust Bees"
		},
		{
			typeof(DocileLocustBeesAI),
			"Docile Locust Bees"
		},
		{
			typeof(DoublewingAI),
			"Manticoil"
		},
		{
			typeof(RadMechAI),
			"Old Bird"
		},
		{
			typeof(BushWolfEnemy),
			"Bush Wolf"
		},
		{
			typeof(GiantKiwiAI),
			"GiantKiwi"
		},
		{
			typeof(PumaAI),
			"Feiopar"
		},
		{
			typeof(StingrayAI),
			"Backwater Gunkfish"
		},
		{
			typeof(CadaverBloomAI),
			"Cadaver Bloom"
		}
	};

	private static FieldInfo visibleId;

	private static FieldInfo visibleName;

	private static FieldInfo visiblePlayer;

	private static MethodInfo findRig;

	private static PropertyInfo currentEmote;

	private static HashSet<int> specialEmotes;

	private static readonly FieldInfo NutcrackerInspecting = AccessTools.Field(typeof(NutcrackerEnemyAI), "isInspecting");

	private static readonly FieldInfo ForestKeeperEating = AccessTools.Field(typeof(ForestGiantAI), "inEatingPlayerAnimation");

	private static readonly FieldInfo ManeaterScrapEaten = AccessTools.Field(typeof(CaveDwellerAI), "scrapEaten");

	private static readonly FieldInfo ManeaterTransforming = AccessTools.Field(typeof(CaveDwellerAI), "inTransformingAnimation");

	private static readonly FieldInfo ManeaterLeaping = AccessTools.Field(typeof(CaveDwellerAI), "leaping");

	private static readonly FieldInfo SpiderReachedWallPosition = AccessTools.Field(typeof(SandSpiderAI), "reachedWallPosition");

	private static readonly FieldInfo SpiderWrappingBody = AccessTools.Field(typeof(SandSpiderAI), "spoolingPlayerBody");

	private static readonly FieldInfo CoilHeadOnCooldown = AccessTools.Field(typeof(SpringManAI), "setOnCooldown");

	private static readonly FieldInfo FoxDragging = AccessTools.Field(typeof(BushWolfEnemy), "dragging");

	private static readonly FieldInfo FoxKilling = AccessTools.Field(typeof(BushWolfEnemy), "inKillAnimation");

	private static readonly FieldInfo FeioparRelocating = AccessTools.Field(typeof(PumaAI), "relocatingToNewTree");

	private static readonly FieldInfo BrackenKillAnimation = AccessTools.Field(typeof(FlowermanAI), "killAnimationCoroutine");

	private static PropertyInfo brackenDataInstance;

	private static PropertyInfo brackenBindings;

	private static PropertyInfo brackenExecutionDistance;

	private static readonly PayoutLedger ledger = new PayoutLedger();

	private static readonly ConditionalWeakTable<object, PhotoShot> shots = new ConditionalWeakTable<object, PhotoShot>();

	private static NetworkManager currentNetwork;

	private static StartOfRound currentRound;

	private static long epoch;

	private static FieldInfo cameraField;

	private static FieldInfo pictureField;

	private static FieldInfo photoIdField;

	private static MethodInfo detectMethod;

	private static MethodInfo legacyPrice;

	private static MethodInfo descriptionMethod;

	private static FieldInfo cameraDetector;

	private static FieldInfo detectorCamera;

	private static FieldInfo detectorMask;

	private static MethodInfo detectorInRange;

	private static MethodInfo detectorInViewport;

	private static Type visibleEntityType;

	private static readonly Dictionary<Vector3, ulong> growthIds = new Dictionary<Vector3, ulong>();

	private static readonly Dictionary<ulong, GhostPhoto> ghostPhotos = new Dictionary<ulong, GhostPhoto>();

	private static CustomMessagingManager ghostMessages;

	private Harmony harmony;

	private void Awake()
	{
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Expected O, but got Unknown
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Expected O, but got Unknown
		log = ((BaseUnityPlugin)this).Logger;
		try
		{
			string text = Path.Combine(Paths.ConfigPath, "Wesley.LethalSnapRewards.cfg");
			Directory.CreateDirectory(Paths.ConfigPath);
			if (!File.Exists(text))
			{
				using (Stream stream = typeof(RewardsPreviewPlugin).Assembly.GetManifestResourceStream("LethalSnapRewards.Defaults.cfg"))
				{
					if (stream == null)
					{
						throw new InvalidOperationException("Missing embedded reward settings.");
					}
					using FileStream destination = new FileStream(text, FileMode.CreateNew, FileAccess.Write);
					stream.CopyTo(destination);
				}
				log.LogInfo((object)("[LSREWARD] Created config " + text));
			}
			string text2 = File.ReadAllText(text);
			settings = RewardSettings.Parse(text2, (Action<string>)delegate(string message)
			{
				log.LogWarning((object)("[LSREWARD] " + message));
			});
			calculator = new PhotoRewardCalculator(settings);
			Type type = AccessTools.TypeByName("LethalSnapProject.Behaviour.DetectionSystem");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "GetVisibleEntities", (Type[])null, (Type[])null));
			if (methodInfo == null || !methodInfo.ReturnType.IsGenericType)
			{
				throw new MissingMethodException("LethalSnapReborn.GetVisibleEntities");
			}
			Type type2 = (visibleEntityType = methodInfo.ReturnType.GetGenericArguments()[0]);
			visibleId = AccessTools.Field(type2, "networkID");
			visibleName = AccessTools.Field(type2, "name");
			visiblePlayer = AccessTools.Field(type2, "isPlayer");
			if (visibleId == null || visibleName == null || visiblePlayer == null || visibleId.FieldType != typeof(ulong) || visibleName.FieldType != typeof(string) || visiblePlayer.FieldType != typeof(bool))
			{
				throw new MissingMemberException("LethalSnapReborn.VisibleEntity layout");
			}
			bool flag = ConfigureEmotes();
			bool flag2 = ConfigureBracken();
			harmony = new Harmony("local.wesley.lethalsnap.rewards.preview");
			InstallPatches(harmony, methodInfo);
			log.LogInfo((object)("[LSREWARD] READY version=1.0.0 mode=PAYOUT emoteReader=" + (flag ? "READY" : "UNAVAILABLE") + " brackenReader=" + (flag2 ? "READY" : "UNAVAILABLE")));
		}
		catch (Exception ex)
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			log.LogError((object)("[LSREWARD] DISABLED " + ex));
		}
	}

	private static bool ConfigureBracken()
	{
		if (!Chainloader.PluginInfos.TryGetValue("Ovchinikov.SnatchinBracken.Main", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			return false;
		}
		Type type = ((object)value.Instance).GetType().Assembly.GetType("SnatchinBracken.Patches.data.SharedData");
		brackenBindings = ((type == null) ? null : type.GetProperty("BindedDrags"));
		brackenExecutionDistance = ((type == null) ? null : type.GetProperty("DistanceFromFavorite"));
		if (brackenBindings == null || brackenBindings.PropertyType != typeof(Dictionary<FlowermanAI, PlayerControllerB>) || brackenExecutionDistance == null || brackenExecutionDistance.PropertyType != typeof(float) || BrackenKillAnimation == null || BrackenKillAnimation.FieldType != typeof(Coroutine))
		{
			return false;
		}
		PropertyInfo property = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
		if (property == null || property.PropertyType != type)
		{
			return false;
		}
		brackenDataInstance = property;
		return true;
	}

	private static bool ConfigureEmotes()
	{
		if (!Chainloader.PluginInfos.TryGetValue("com.seeya.muchbetteremotes", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			log.LogWarning((object)"[LSREWARD] MuchBetterEmotes unavailable.");
			return false;
		}
		if (value.Metadata.Version != new Version(1, 0, 0))
		{
			log.LogWarning((object)("[LSREWARD] Unsupported MuchBetterEmotes version " + value.Metadata.Version));
			return false;
		}
		Assembly assembly = ((object)value.Instance).GetType().Assembly;
		Type type = assembly.GetType("MuchBetterEmotes.EmoteRig");
		Type type2 = assembly.GetType("MuchBetterEmotes.EmoteDefs");
		MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Of", new Type[1] { typeof(PlayerControllerB) }, (Type[])null));
		PropertyInfo propertyInfo = ((type == null) ? null : AccessTools.Property(type, "Current"));
		if (type2 == null || methodInfo == null || !methodInfo.IsStatic || methodInfo.ReturnType != type || propertyInfo == null || propertyInfo.PropertyType != typeof(int))
		{
			log.LogWarning((object)"[LSREWARD] Unsupported MuchBetterEmotes rig layout.");
			return false;
		}
		HashSet<int> hashSet = new HashSet<int>();
		string[] array = new string[14]
		{
			"MiddleFinger", "Clap", "Shy", "Griddy", "Twerk", "Salute", "Prisyadka", "Sign", "ThumbsUp", "Wave",
			"SixSeven", "DoubleMiddleFinger", "DoubleClap", "SignShown"
		};
		foreach (string text in array)
		{
			FieldInfo fieldInfo = AccessTools.Field(type2, text);
			if (fieldInfo == null || !fieldInfo.IsLiteral || fieldInfo.FieldType != typeof(int))
			{
				log.LogWarning((object)("[LSREWARD] Missing special emote definition " + text));
				return false;
			}
			hashSet.Add((int)fieldInfo.GetRawConstantValue());
		}
		findRig = methodInfo;
		currentEmote = propertyInfo;
		specialEmotes = hashSet;
		return true;
	}

	private static void InstallPatches(Harmony patches, MethodInfo detect)
	{
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Expected O, but got Unknown
		//IL_02cd: Expected O, but got Unknown
		//IL_02cd: Expected O, but got Unknown
		//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ef: Expected O, but got Unknown
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_0311: Expected O, but got Unknown
		//IL_033a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0347: Expected O, but got Unknown
		//IL_038b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0399: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("LethalSnapProject.Behaviour.PolaroidItem");
		Type type2 = AccessTools.TypeByName("LethalSnapProject.Behaviour.PictureHelper");
		Type type3 = AccessTools.TypeByName("LethalSnapProject.Behaviour.PictureItem");
		if (type == null || type2 == null || type3 == null)
		{
			throw new TypeLoadException("LethalSnapReborn picture types");
		}
		MethodInfo methodInfo = AccessTools.Method(type, "SpawnPicture", (Type[])null, (Type[])null);
		Type type4 = ((methodInfo == null) ? null : ((AsyncStateMachineAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(AsyncStateMachineAttribute))))?.StateMachineType;
		if (type4 == null || type4.IsValueType)
		{
			throw new TypeLoadException("LethalSnapReborn SpawnPicture state machine");
		}
		cameraField = AccessTools.Field(type4, "<>4__this");
		pictureField = AccessTools.Field(type4, "<picture>5__5");
		photoIdField = AccessTools.Field(type4, "pictureID");
		cameraDetector = AccessTools.Field(type, "detectionSystem");
		detectorCamera = AccessTools.Field(detect.DeclaringType, "_camera");
		detectorMask = AccessTools.Field(detect.DeclaringType, "_layerMask");
		detectorInRange = AccessTools.Method(detect.DeclaringType, "IsInRange", (Type[])null, (Type[])null);
		detectorInViewport = AccessTools.Method(detect.DeclaringType, "IsInViewPort", (Type[])null, (Type[])null);
		detectMethod = detect;
		legacyPrice = AccessTools.Method(type2, "ComputePictureValue", new Type[2]
		{
			detect.ReturnType,
			typeof(bool)
		}, (Type[])null);
		descriptionMethod = AccessTools.Method(type2, "MakePictureDescription", new Type[2]
		{
			detect.ReturnType,
			typeof(bool)
		}, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(type4, "MoveNext", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(StartOfRound), "SetShipReadyToLand", (Type[])null, (Type[])null);
		MethodInfo methodInfo4 = AccessTools.Method(typeof(StartOfRound), "OnDestroy", (Type[])null, (Type[])null);
		if (cameraField == null || pictureField == null || pictureField.FieldType != type3 || photoIdField == null || legacyPrice == null || legacyPrice.ReturnType != typeof(int) || descriptionMethod == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null)
		{
			throw new MissingMemberException("LethalSnapReborn photo payout hooks");
		}
		patches.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(RewardsPreviewPlugin), "BeginPhotoStep", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RewardsPreviewPlugin), "PatchPhotoCalls", (Type[])null), new HarmonyMethod(typeof(RewardsPreviewPlugin), "EndPhotoStep", (Type[])null), (HarmonyMethod)null);
		patches.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(RewardsPreviewPlugin), "OnOrbitReturned", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		patches.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(RewardsPreviewPlugin), "OnLobbyEnded", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		patches.Patch((MethodBase)AccessTools.Method(typeof(StartOfRound), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RewardsPreviewPlugin), "RegisterGhostPhotoHandler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		patches.Patch((MethodBase)AccessTools.Method(type, "TakePictureServerRpc", new Type[2]
		{
			typeof(int),
			typeof(int)
		}, (Type[])null), new HarmonyMethod(typeof(RewardsPreviewPlugin), "SendGuestGhostPhoto", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static IEnumerable<CodeInstruction> PatchPhotoCalls(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
	{
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Expected O, but got Unknown
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Expected O, but got Unknown
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Expected O, but got Unknown
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Expected O, but got Unknown
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Expected O, but got Unknown
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Expected O, but got Unknown
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Expected O, but got Unknown
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Expected O, but got Unknown
		//IL_021c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0226: Expected O, but got Unknown
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Expected O, but got Unknown
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a1: Expected O, but got Unknown
		//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Expected O, but got Unknown
		//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Expected O, but got Unknown
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e1: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(typeof(NetworkObject), "Spawn", new Type[1] { typeof(bool) }, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(RewardsPreviewPlugin), "SnapshotPhoto", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(RewardsPreviewPlugin), "PreparePrice", (Type[])null, (Type[])null);
		MethodInfo methodInfo4 = AccessTools.Method(typeof(RewardsPreviewPlugin), "ConfirmPhoto", (Type[])null, (Type[])null);
		LocalBuilder localBuilder = generator.DeclareLocal(typeof(bool));
		List<CodeInstruction> list = new List<CodeInstruction>();
		int num = 0;
		foreach (CodeInstruction instruction in instructions)
		{
			if (CodeInstructionExtensions.Calls(instruction, detectMethod))
			{
				if (num != 0 || instruction.blocks.Count != 0)
				{
					throw new InvalidOperationException("Unexpected detection call");
				}
				list.Add(instruction);
				list.Add(new CodeInstruction(OpCodes.Dup, (object)null));
				list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo2));
				num = 1;
			}
			else if (CodeInstructionExtensions.Calls(instruction, descriptionMethod))
			{
				if (num != 1 || instruction.blocks.Count != 0)
				{
					throw new InvalidOperationException("Unexpected description call");
				}
				CodeInstruction val = new CodeInstruction(OpCodes.Pop, (object)null);
				val.labels.AddRange(instruction.labels);
				instruction.labels.Clear();
				list.Add(val);
				list.Add(new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
				list.Add(instruction);
			}
			else if (CodeInstructionExtensions.Calls(instruction, legacyPrice))
			{
				if (num != 1 || instruction.blocks.Count != 0)
				{
					throw new InvalidOperationException("Unexpected price call");
				}
				CodeInstruction val2 = new CodeInstruction(OpCodes.Pop, (object)null);
				val2.labels.AddRange(instruction.labels);
				list.Add(val2);
				list.Add(new CodeInstruction(OpCodes.Pop, (object)null));
				list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo3));
				num = 2;
			}
			else if (CodeInstructionExtensions.Calls(instruction, methodInfo))
			{
				if (num != 2 || instruction.blocks.Count != 0)
				{
					throw new InvalidOperationException("Unexpected spawn call");
				}
				CodeInstruction val3 = new CodeInstruction(OpCodes.Stloc, (object)localBuilder);
				val3.labels.AddRange(instruction.labels);
				instruction.labels.Clear();
				list.Add(val3);
				list.Add(new CodeInstruction(OpCodes.Dup, (object)null));
				list.Add(new CodeInstruction(OpCodes.Ldloc, (object)localBuilder));
				list.Add(instruction);
				list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo4));
				num = 3;
			}
			else
			{
				list.Add(instruction);
			}
		}
		if (num != 3)
		{
			throw new InvalidOperationException("Expected detection, price and spawn calls in order");
		}
		return list;
	}

	private static void BeginPhotoStep(object __instance)
	{
		NetworkManager singleton = NetworkManager.Singleton;
		StartOfRound round = StartOfRound.Instance;
		if (!((Object)(object)singleton == (Object)null) && singleton.IsHost && !((Object)(object)round == (Object)null))
		{
			if (!object.ReferenceEquals(singleton, currentNetwork) || !object.ReferenceEquals(round, currentRound))
			{
				ResetPayouts();
				currentNetwork = singleton;
				currentRound = round;
			}
			shots.GetValue(__instance, (object state) => new PhotoShot
			{
				Epoch = epoch,
				Shooter = ((GrabbableObject)cameraField.GetValue(state)).playerHeldBy,
				StartedInExpedition = !round.inShipPhase
			});
		}
	}

	private static bool CanPrice(PhotoShot shot)
	{
		if (shot.StartedInExpedition && shot.Epoch == epoch && (Object)(object)currentNetwork != (Object)null && currentNetwork.IsHost && object.ReferenceEquals(currentNetwork, NetworkManager.Singleton) && (Object)(object)currentRound != (Object)null && object.ReferenceEquals(currentRound, StartOfRound.Instance))
		{
			return !currentRound.inShipPhase;
		}
		return false;
	}

	private static void SnapshotPhoto(IList entities, object state)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Expected O, but got Unknown
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Expected O, but got Unknown
		//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_0337: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03db: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ee: Expected O, but got Unknown
		//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f1: Unknown result type (might be due to invalid IL or missing references)
		if (entities == null || !shots.TryGetValue(state, out var value) || !CanPrice(value) || (Object)(object)value.Shooter == (Object)null || (Object)(object)((GrabbableObject)cameraField.GetValue(state)).playerHeldBy != (Object)(object)value.Shooter)
		{
			return;
		}
		try
		{
			List<MonsterCapture> list = new List<MonsterCapture>();
			List<PlayerCapture> list2 = new List<PlayerCapture>();
			ulong? num = ConsumeGuestGhostPhoto(value.Shooter, (GrabbableObject)cameraField.GetValue(state), (int)photoIdField.GetValue(state));
			for (int i = 0; i < entities.Count; i++)
			{
				object obj = entities[i];
				ulong num2 = (ulong)visibleId.GetValue(obj);
				if (!currentNetwork.SpawnManager.SpawnedObjects.TryGetValue(num2, out var value2) || (Object)(object)value2 == (Object)null)
				{
					continue;
				}
				if ((bool)visiblePlayer.GetValue(obj))
				{
					if (num2 != ((NetworkBehaviour)value.Shooter).NetworkObjectId)
					{
						PlayerControllerB component = ((Component)value2).GetComponent<PlayerControllerB>();
						if (!((Object)(object)component == (Object)null))
						{
							int? num3 = (settings.EmoteEnabled ? ReadEmote(component) : ((int?)null));
							bool flag = settings.EmoteEnabled && (IsVanillaEmote(component) || (num3.HasValue && specialEmotes.Contains(num3.Value)));
							list2.Add(new PlayerCapture(num2, component.health, flag, (CapturePosition?)PositionOf(((Component)component).transform.position)));
						}
					}
					continue;
				}
				EnemyAI component2 = ((Component)value2).GetComponent<EnemyAI>();
				if ((Object)(object)component2 == (Object)null || component2.isEnemyDead)
				{
					continue;
				}
				if (component2 is CadaverGrowthAI)
				{
					entities.RemoveAt(i--);
					continue;
				}
				if (((object)component2).GetType().FullName == "Observers.ObserversAI")
				{
					bool flag2 = !component2.isEnemyDead && component2.currentBehaviourStateIndex == 2;
					log.LogInfo((object)("[LSREWARD] OBSERVER netId=" + num2 + " state=" + component2.currentBehaviourStateIndex + " activeChaser=" + flag2));
					if (!flag2)
					{
						continue;
					}
				}
				CaveDwellerAI val = (CaveDwellerAI)(object)((component2 is CaveDwellerAI) ? component2 : null);
				bool flag3 = (Object)(object)val != (Object)null && ManeaterTransforming != null && (bool)ManeaterTransforming.GetValue(val);
				bool flag4 = (Object)(object)val != (Object)null && ((EnemyAI)val).currentBehaviourStateIndex != 0 && !flag3;
				string text = (string)visibleName.GetValue(obj);
				string text2 = (((Object)(object)component2.enemyType != (Object)null) ? component2.enemyType.enemyName : text);
				MonsterSpecialState val2 = ReadMonsterSpecialState(component2, value.Shooter, flag3, flag4);
				if (component2 is DressGirlAI && !component2.isEnemyDead && num == num2)
				{
					val2 = (MonsterSpecialState)24;
				}
				ulong num4 = (uint)((Object)value2).GetInstanceID();
				int num5 = (((Object)(object)val != (Object)null && !flag4 && ManeaterScrapEaten != null) ? ((int)ManeaterScrapEaten.GetValue(val)) : 0);
				SandWormAI val3 = (SandWormAI)(object)((component2 is SandWormAI) ? component2 : null);
				bool flag5 = (Object)(object)val3 != (Object)null && !val3.emerged;
				VanillaPriceNames.TryGetValue(((object)component2).GetType(), out var value3);
				string text3 = settings.ResolveMonsterPriceName(text2 ?? text, value3);
				list.Add(new MonsterCapture(num4, text3, (CapturePosition?)PositionOf(((Component)value2).transform.position), val2, false, num5, flag4, flag5));
				if ((Object)(object)val3 != (Object)null)
				{
					log.LogInfo((object)("[LSREWARD] LEVIATHAN netId=" + num2 + " emerged=" + val3.emerged));
				}
				if ((Object)(object)val != (Object)null)
				{
					log.LogInfo((object)("[LSREWARD] MANEATER netId=" + num2 + " form=" + (flag3 ? "TRANSFORMING" : (flag4 ? "ADULT" : "BABY")) + " scrapEaten=" + num5));
				}
				if ((int)val2 != 0)
				{
					log.LogInfo((object)("[LSREWARD] SPECIAL netId=" + num2 + " monster=" + text + " state=" + val2));
				}
			}
			CaptureCadaverGrowths(cameraField.GetValue(state), entities, list);
			value.Reward = calculator.Calculate((IEnumerable<MonsterCapture>)list, (IEnumerable<PlayerCapture>)list2, (ulong?)((NetworkBehaviour)value.Shooter).NetworkObjectId);
		}
		catch (Exception ex)
		{
			log.LogError((object)("[LSREWARD] SNAPSHOT_ERROR " + ex));
		}
	}

	private static int PreparePrice(object state)
	{
		if (!shots.TryGetValue(state, out var value) || !CanPrice(value) || value.Reward == null)
		{
			return 0;
		}
		try
		{
			value.Payout = ledger.Prepare(value.Reward.Appraisals);
			return value.Payout.Value;
		}
		catch (Exception ex)
		{
			log.LogError((object)("[LSREWARD] PRICE_ERROR " + ex));
			return 0;
		}
	}

	private static void CaptureCadaverGrowths(object polaroid, IList entities, List<MonsterCapture> monsters)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: 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)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: 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_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: 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_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Expected O, but got Unknown
		if (cameraDetector == null || detectorCamera == null || detectorMask == null || detectorInRange == null || detectorInViewport == null)
		{
			return;
		}
		object value = cameraDetector.GetValue(polaroid);
		if (value == null)
		{
			return;
		}
		object? value2 = detectorCamera.GetValue(value);
		Camera val = (Camera)((value2 is Camera) ? value2 : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Vector3 position = ((Component)val).transform.position;
		int num = LayerMask.op_Implicit((LayerMask)detectorMask.GetValue(value));
		HashSet<ulong> hashSet = new HashSet<ulong>();
		object[] array = new object[2];
		object[] array2 = new object[1];
		CadaverGrowthAI[] array3 = Object.FindObjectsOfType<CadaverGrowthAI>();
		foreach (CadaverGrowthAI val2 in array3)
		{
			if (val2.GrowthTiles == null)
			{
				continue;
			}
			foreach (TileWithGrowth growthTile in val2.GrowthTiles)
			{
				if (growthTile == null || growthTile.eradicated || growthTile.plantPositions == null)
				{
					continue;
				}
				foreach (Vector3 plantPosition in growthTile.plantPositions)
				{
					Vector3 val3 = plantPosition + Vector3.up * 0.35f;
					float num2 = Vector3.Distance(position, val3);
					array[0] = val3;
					array[1] = num2;
					array2[0] = val3;
					if ((bool)detectorInRange.Invoke(value, array) && (bool)detectorInViewport.Invoke(value, array2) && !Physics.Linecast(position, val3, num, (QueryTriggerInteraction)1))
					{
						if (!growthIds.TryGetValue(plantPosition, out var value3))
						{
							value3 = (ulong)(long.MinValue | (uint)growthIds.Count);
							growthIds.Add(plantPosition, value3);
						}
						if (hashSet.Add(value3))
						{
							monsters.Add(new MonsterCapture(value3, "Cadaver Growth", (CapturePosition?)PositionOf(plantPosition), (MonsterSpecialState)0, false, 0, false, false));
							object obj = Activator.CreateInstance(visibleEntityType);
							visibleId.SetValue(obj, value3);
							visibleName.SetValue(obj, "Cadaver Growth");
							entities.Add(obj);
						}
					}
				}
			}
		}
		if (hashSet.Count > 0)
		{
			log.LogInfo((object)("[LSREWARD] GROWTHS count=" + hashSet.Count));
		}
	}

	private static void RegisterGhostPhotoHandler(StartOfRound __instance)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Expected O, but got Unknown
		NetworkManager singleton = NetworkManager.Singleton;
		if (!((Object)(object)singleton == (Object)null) && singleton.IsHost && singleton.CustomMessagingManager != null)
		{
			ResetPayouts();
			currentNetwork = singleton;
			currentRound = __instance;
			if (ghostMessages != null)
			{
				ghostMessages.UnregisterNamedMessageHandler("wesley.lethalsnap.rewards.ghost-photo.v1");
			}
			ghostMessages = singleton.CustomMessagingManager;
			ghostMessages.RegisterNamedMessageHandler("wesley.lethalsnap.rewards.ghost-photo.v1", new HandleNamedMessageDelegate(ReceiveGuestGhostPhoto));
		}
	}

	private unsafe static void SendGuestGhostPhoto(GrabbableObject __instance, int id, int cullingMask)
	{
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsClient || singleton.IsHost || singleton.CustomMessagingManager == null)
		{
			return;
		}
		PlayerControllerB playerHeldBy = __instance.playerHeldBy;
		if ((Object)(object)playerHeldBy == (Object)null || !((NetworkBehaviour)playerHeldBy).IsOwner || playerHeldBy.isPlayerDead)
		{
			return;
		}
		DressGirlAI[] array = Object.FindObjectsOfType<DressGirlAI>();
		FastBufferWriter val2 = default(FastBufferWriter);
		foreach (DressGirlAI val in array)
		{
			if (IsVisibleChasingGhost(val, playerHeldBy, cullingMask))
			{
				((FastBufferWriter)(ref val2))..ctor(20, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val2)).WriteBytesSafe(BitConverter.GetBytes(id), 4, 0);
					((FastBufferWriter)(ref val2)).WriteBytesSafe(BitConverter.GetBytes(((NetworkBehaviour)__instance).NetworkObjectId), 8, 0);
					((FastBufferWriter)(ref val2)).WriteBytesSafe(BitConverter.GetBytes(((NetworkBehaviour)val).NetworkObjectId), 8, 0);
					singleton.CustomMessagingManager.SendNamedMessage("wesley.lethalsnap.rewards.ghost-photo.v1", 0uL, val2, (NetworkDelivery)3);
					break;
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose();
				}
			}
		}
	}

	private static void ReceiveGuestGhostPhoto(ulong sender, FastBufferReader reader)
	{
		if (!((Object)(object)currentNetwork == (Object)null) && currentNetwork.IsHost && !((Object)(object)currentRound == (Object)null) && !currentRound.inShipPhase && currentNetwork.ConnectedClients.ContainsKey(sender) && ((FastBufferReader)(ref reader)).Length == 20)
		{
			byte[] value = ((FastBufferReader)(ref reader)).ToArray();
			ghostPhotos[sender] = new GhostPhoto
			{
				PhotoId = BitConverter.ToInt32(value, 0),
				CameraId = BitConverter.ToUInt64(value, 4),
				GhostId = BitConverter.ToUInt64(value, 12),
				ReceivedAt = Time.realtimeSinceStartup
			};
		}
	}

	private static ulong? ConsumeGuestGhostPhoto(PlayerControllerB shooter, GrabbableObject camera, int photoId)
	{
		ulong actualClientId = shooter.actualClientId;
		if (!ghostPhotos.TryGetValue(actualClientId, out var value) || value.PhotoId != photoId || value.CameraId != ((NetworkBehaviour)camera).NetworkObjectId)
		{
			return null;
		}
		ghostPhotos.Remove(actualClientId);
		if (Time.realtimeSinceStartup - value.ReceivedAt > 5f || !currentNetwork.SpawnManager.SpawnedObjects.TryGetValue(value.GhostId, out var value2) || (Object)(object)value2 == (Object)null || value2.OwnerClientId != actualClientId)
		{
			return null;
		}
		return value.GhostId;
	}

	private static void ConfirmPhoto(NetworkObject spawned, object state)
	{
		if (shots.TryGetValue(state, out var value) && value.Payout != null && CanPrice(value) && !((Object)(object)spawned == (Object)null) && spawned.IsSpawned)
		{
			object? value2 = pictureField.GetValue(state);
			GrabbableObject val = (GrabbableObject)((value2 is GrabbableObject) ? value2 : null);
			if ((Object)(object)val == (Object)null || (Object)(object)spawned != (Object)(object)((NetworkBehaviour)val).NetworkObject || val.scrapValue != value.Payout.Value)
			{
				log.LogError((object)"[LSREWARD] Spawned picture or price did not match prepared payout");
			}
			else if (ledger.Complete(value.Payout, true))
			{
				value.Payout = null;
				PhotoReward reward = value.Reward;
				log.LogInfo((object)string.Concat("[LSREWARD] PAID photo=", photoIdField.GetValue(state), " scrapValue=", val.scrapValue, " monsters=", reward.Appraisals.Count, " countBonus=", Number(reward.PlayerCountBonus), " emoteBonus=", Number(reward.EmoteBonus), " healthBonus=", Number(reward.HealthBonus), " monsterCountBonus=", Number(reward.MonsterCountBonus), " commonMultiplier=", Number(reward.CommonMultiplier)));
			}
		}
	}

	private static Exception EndPhotoStep(object __instance, Exception __exception)
	{
		if (shots.TryGetValue(__instance, out var value) && value.Payout != null)
		{
			ledger.Complete(value.Payout, false);
			value.Payout = null;
		}
		return __exception;
	}

	private static void OnOrbitReturned()
	{
		ResetPayouts();
		if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)
		{
			log.LogInfo((object)"[LSREWARD] RESET reason=ORBIT");
		}
	}

	private static void OnLobbyEnded()
	{
		if (ghostMessages != null)
		{
			ghostMessages.UnregisterNamedMessageHandler("wesley.lethalsnap.rewards.ghost-photo.v1");
		}
		ghostMessages = null;
		ResetPayouts();
		currentNetwork = null;
		currentRound = null;
	}

	private static void ResetPayouts()
	{
		epoch++;
		ledger.Reset();
		growthIds.Clear();
		ghostPhotos.Clear();
	}

	private static MonsterSpecialState ReadMonsterSpecialState(EnemyAI enemy, PlayerControllerB shooter, bool maneaterTransforming, bool maneaterAdult)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0287: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Invalid comparison between Unknown and I4
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_0391: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead)
		{
			return (MonsterSpecialState)0;
		}
		if (enemy.currentBehaviourStateIndex == 2)
		{
			switch (((object)enemy).GetType().FullName)
			{
			case "Locker.MonoBehaviours.LockerAI":
				return (MonsterSpecialState)27;
			case "ShyGuy.AI.ShyGuyAI":
				return (MonsterSpecialState)28;
			}
		}
		FlowermanAI val = (FlowermanAI)(object)((enemy is FlowermanAI) ? enemy : null);
		if ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)((EnemyAI)val).enemyType != (Object)null && string.Equals(((EnemyAI)val).enemyType.enemyName, "Floating head", StringComparison.OrdinalIgnoreCase))
			{
				return (MonsterSpecialState)0;
			}
			return ReadBrackenSpecialState(val);
		}
		DressGirlAI val2 = (DressGirlAI)(object)((enemy is DressGirlAI) ? enemy : null);
		if ((Object)(object)val2 != (Object)null && (Object)(object)shooter.gameplayCamera != (Object)null && IsVisibleChasingGhost(val2, shooter, shooter.gameplayCamera.cullingMask))
		{
			return (MonsterSpecialState)24;
		}
		JesterAI val3 = (JesterAI)(object)((enemy is JesterAI) ? enemy : null);
		if ((Object)(object)val3 != (Object)null && ((EnemyAI)val3).currentBehaviourStateIndex == 2)
		{
			return (MonsterSpecialState)1;
		}
		if (enemy is ForestGiantAI && ForestKeeperEating != null && (bool)ForestKeeperEating.GetValue(enemy))
		{
			return (MonsterSpecialState)2;
		}
		if (enemy is ButlerEnemyAI && (Object)(object)enemy.creatureAnimator != (Object)null && enemy.creatureAnimator.GetInteger("HeldItem") == 1)
		{
			return (MonsterSpecialState)18;
		}
		SandSpiderAI val4 = (SandSpiderAI)(object)((enemy is SandSpiderAI) ? enemy : null);
		if ((Object)(object)val4 != (Object)null)
		{
			bool flag = SpiderWrappingBody != null && (bool)SpiderWrappingBody.GetValue(val4);
			bool flag2 = ((EnemyAI)val4).currentBehaviourStateIndex == 1 && val4.lookingForWallPosition && SpiderReachedWallPosition != null && (bool)SpiderReachedWallPosition.GetValue(val4);
			return HigherSpecial((MonsterSpecialState)(flag ? 15 : 0), (MonsterSpecialState)(flag2 ? 14 : 0));
		}
		CentipedeAI val5 = (CentipedeAI)(object)((enemy is CentipedeAI) ? enemy : null);
		if ((Object)(object)val5 != (Object)null && (Object)(object)val5.clingingToPlayer != (Object)null && !val5.clingingToPlayer.isPlayerDead)
		{
			return (MonsterSpecialState)10;
		}
		FlowerSnakeEnemy val6 = (FlowerSnakeEnemy)(object)((enemy is FlowerSnakeEnemy) ? enemy : null);
		if ((Object)(object)val6 != (Object)null && IsTulipSnakeLiftingPlayer(val6))
		{
			return (MonsterSpecialState)19;
		}
		BushWolfEnemy val7 = (BushWolfEnemy)(object)((enemy is BushWolfEnemy) ? enemy : null);
		if ((Object)(object)val7 != (Object)null && (Object)(object)val7.draggingPlayer != (Object)null && !val7.draggingPlayer.isPlayerDead && FoxDragging != null && (bool)FoxDragging.GetValue(val7) && FoxKilling != null && !(bool)FoxKilling.GetValue(val7))
		{
			return (MonsterSpecialState)20;
		}
		PumaAI val8 = (PumaAI)(object)((enemy is PumaAI) ? enemy : null);
		if ((Object)(object)val8 != (Object)null && (int)val8.treeState == 4 && FeioparRelocating != null && !(bool)FeioparRelocating.GetValue(val8))
		{
			return (MonsterSpecialState)21;
		}
		CadaverBloomAI val9 = (CadaverBloomAI)(object)((enemy is CadaverBloomAI) ? enemy : null);
		if ((Object)(object)val9 != (Object)null && (Object)(object)val9.burstPlayer != (Object)null && val9.hasBurst && ((EnemyAI)val9).inSpecialAnimation)
		{
			return (MonsterSpecialState)22;
		}
		MaskedPlayerEnemy val10 = (MaskedPlayerEnemy)(object)((enemy is MaskedPlayerEnemy) ? enemy : null);
		if ((Object)(object)val10 != (Object)null && ((EnemyAI)val10).inSpecialAnimation && (Object)(object)((EnemyAI)val10).inSpecialAnimationWithPlayer != (Object)null && !((EnemyAI)val10).inSpecialAnimationWithPlayer.isPlayerDead && (Object)(object)val10.maskFloodParticle != (Object)null && val10.maskFloodParticle.isEmitting)
		{
			return (MonsterSpecialState)23;
		}
		SpringManAI val11 = (SpringManAI)(object)((enemy is SpringManAI) ? enemy : null);
		if ((Object)(object)val11 != (Object)null)
		{
			return HigherSpecial((MonsterSpecialState)((CoilHeadOnCooldown != null && (bool)CoilHeadOnCooldown.GetValue(val11)) ? 17 : 0), (MonsterSpecialState)((((EnemyAI)val11).stunNormalizedTimer > 0f) ? 16 : 0));
		}
		CaveDwellerAI val12 = (CaveDwellerAI)(object)((enemy is CaveDwellerAI) ? enemy : null);
		if ((Object)(object)val12 != (Object)null)
		{
			if (maneaterTransforming)
			{
				return (MonsterSpecialState)12;
			}
			if (maneaterAdult && ManeaterLeaping != null && (bool)ManeaterLeaping.GetValue(val12))
			{
				return (MonsterSpecialState)13;
			}
			if (!maneaterAdult && val12.holdingBaby)
			{
				return (MonsterSpecialState)11;
			}
		}
		GiantKiwiAI val13 = (GiantKiwiAI)(object)((enemy is GiantKiwiAI) ? enemy : null);
		if ((Object)(object)val13 != (Object)null && ((EnemyAI)val13).currentBehaviourStateIndex == 2)
		{
			return (MonsterSpecialState)3;
		}
		if (enemy is MouthDogAI && enemy.currentBehaviourStateIndex == 3)
		{
			return (MonsterSpecialState)4;
		}
		if (enemy is NutcrackerEnemyAI)
		{
			if (enemy.currentBehaviourStateIndex == 2)
			{
				return (MonsterSpecialState)9;
			}
			if (enemy.currentBehaviourStateIndex == 1 && NutcrackerInspecting != null && (bool)NutcrackerInspecting.GetValue(enemy))
			{
				return (MonsterSpecialState)8;
			}
		}
		HoarderBugAI val14 = (HoarderBugAI)(object)((enemy is HoarderBugAI) ? enemy : null);
		if ((Object)(object)val14 != (Object)null)
		{
			GrabbableObject val15 = ((val14.heldItem == null) ? null : val14.heldItem.itemGrabbableObject);
			bool flag3 = (Object)(object)val15 != (Object)null && (Object)(object)val15.itemProperties != (Object)null && val15.itemProperties.isScrap;
			bool flag4 = ((EnemyAI)val14).currentBehaviourStateIndex == 2;
			if (flag3 && flag4)
			{
				return (MonsterSpecialState)7;
			}
			if (flag3)
			{
				return (MonsterSpecialState)5;
			}
			if (flag4)
			{
				return (MonsterSpecialState)6;
			}
		}
		return (MonsterSpecialState)0;
	}

	private static bool IsVanillaEmote(PlayerControllerB player)
	{
		if (player.isPlayerDead || !player.performingEmote || (Object)(object)player.playerBodyAnimator == (Object)null)
		{
			return false;
		}
		int integer = player.playerBodyAnimator.GetInteger("emoteNumber");
		if (integer != 1)
		{
			return integer == 2;
		}
		return true;
	}

	private static int? ReadEmote(PlayerControllerB player)
	{
		if (findRig == null)
		{
			return null;
		}
		object obj = findRig.Invoke(null, new object[1] { player });
		if ((Object)((obj is Object) ? obj : null) == (Object)null)
		{
			return null;
		}
		return (int)currentEmote.GetValue(obj, null);
	}

	private static MonsterSpecialState ReadBrackenSpecialState(FlowermanAI bracken)
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		if (brackenDataInstance == null || (Object)(object)bracken.bodyBeingCarried != (Object)null)
		{
			return (MonsterSpecialState)0;
		}
		object value = brackenDataInstance.GetValue(null, null);
		if (value == null)
		{
			return (MonsterSpecialState)0;
		}
		Dictionary<FlowermanAI, PlayerControllerB> dictionary = (Dictionary<FlowermanAI, PlayerControllerB>)brackenBindings.GetValue(value, null);
		PlayerControllerB value2;
		bool flag = dictionary != null && dictionary.TryGetValue(bracken, out value2) && (Object)(object)value2 != (Object)null && !value2.isPlayerDead;
		PlayerControllerB inSpecialAnimationWithPlayer = ((EnemyAI)bracken).inSpecialAnimationWithPlayer;
		bool flag2 = bracken.inKillAnimation && (Object)(object)inSpecialAnimationWithPlayer != (Object)null && !inSpecialAnimationWithPlayer.isPlayerDead && (Object)(object)((EnemyAI)bracken).favoriteSpot != (Object)null && BrackenKillAnimation.GetValue(bracken) != null && Vector3.Distance(((Component)bracken).transform.position, ((EnemyAI)bracken).favoriteSpot.position) <= (float)brackenExecutionDistance.GetValue(value, null);
		return HigherSpecial((MonsterSpecialState)(flag ? 25 : 0), (MonsterSpecialState)(flag2 ? 26 : 0));
	}

	private static MonsterSpecialState HigherSpecial(MonsterSpecialState first, MonsterSpecialState second)
	{
		//IL_0000: 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_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if ((int)first == 0)
		{
			return second;
		}
		if (!(settings.SpecialBonus(second) > settings.SpecialBonus(first)))
		{
			return first;
		}
		return second;
	}

	private static bool IsVisibleChasingGhost(DressGirlAI ghost, PlayerControllerB shooter, int cameraMask)
	{
		if (((NetworkBehaviour)ghost).IsOwner && !((EnemyAI)ghost).isEnemyDead && (Object)(object)ghost.hauntingPlayer == (Object)(object)shooter && ((EnemyAI)ghost).currentBehaviourStateIndex == 1)
		{
			if (!HasVisibleMesh((Renderer[])(object)((EnemyAI)ghost).skinnedMeshRenderers, cameraMask))
			{
				return HasVisibleMesh((Renderer[])(object)((EnemyAI)ghost).meshRenderers, cameraMask);
			}
			return true;
		}
		return false;
	}

	private static bool HasVisibleMesh(Renderer[] renderers, int cameraMask)
	{
		if (renderers == null)
		{
			return false;
		}
		foreach (Renderer val in renderers)
		{
			if ((Object)(object)val != (Object)null && val.enabled && ((Component)val).gameObject.activeInHierarchy && (cameraMask & (1 << ((Component)val).gameObject.layer)) != 0)
			{
				return true;
			}
		}
		return false;
	}

	private static bool IsTulipSnakeLiftingPlayer(FlowerSnakeEnemy snake)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		PlayerControllerB clingingToPlayer = snake.clingingToPlayer;
		if (!snake.activatedFlight || (Object)(object)clingingToPlayer == (Object)null || clingingToPlayer.isPlayerDead || !clingingToPlayer.jetpackControls || clingingToPlayer.disablingJetpackControls)
		{
			return false;
		}
		if (((NetworkBehaviour)clingingToPlayer).IsOwner)
		{
			if ((Object)(object)clingingToPlayer.thisController != (Object)null)
			{
				return !clingingToPlayer.thisController.isGrounded;
			}
			return false;
		}
		Vector3 val = ((Component)clingingToPlayer).transform.position + Vector3.up * 0.2f;
		return !Physics.Raycast(val, Vector3.down, 0.45f, currentRound.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1);
	}

	private static CapturePosition PositionOf(Vector3 position)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		return new CapturePosition((double)position.x, (double)position.y, (double)position.z);
	}

	private static string Number(decimal value)
	{
		return value.ToString(CultureInfo.InvariantCulture);
	}
}

plugins/LethalSnapRewardsAddon/LethalSnapRewards.Core.dll

Decompiled 25 minutes ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Wesley.LethalSnapRewards;

public sealed class RewardSettings
{
	private sealed class ConfigReader
	{
		private readonly IDictionary<string, string> entries;

		private readonly Action<string> warn;

		public ConfigReader(IDictionary<string, string> entries, Action<string> warn)
		{
			this.entries = entries;
			this.warn = warn;
		}

		public string Text(string key, string fallback)
		{
			if (!entries.TryGetValue(key, out var value))
			{
				return fallback;
			}
			return value;
		}

		public void Invalid(string key)
		{
			if (warn != null)
			{
				warn(key + ": 설정값이 잘못되어 기본값을 적용했습니다.");
			}
		}

		public bool Flag(string key, bool fallback)
		{
			if (!entries.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (bool.TryParse(value, out var result))
			{
				return result;
			}
			Invalid(key);
			return fallback;
		}

		public decimal Number(string key, decimal fallback, decimal min, decimal max)
		{
			if (!entries.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (TryNumber(value, min, max, out var value2))
			{
				return value2;
			}
			Invalid(key);
			return fallback;
		}
	}

	private const string DefaultValues = "flowerman:150,crawler:90,hoarding bug:10,centipede:20,bunker spider:60,puffer:10,jester:100,blob:5,girl:200,spring:50,nutcracker:80,masked:60,mouthdog:60,earth leviathan:150,forestgiant:60,baboon hawk:15,red locust bees:5,docile locust bees:0,manticoil:0,butler:20,butler bees:5,maneater:20,cavedweller:20,clay surgeon:100,tulip snake:5,old bird:100,bush wolf:40,giantkiwi:40,feiopar:30,backwater gunkfish:5,cadaver bloom:50,cadaver growth:0,peeper:2,locker:70,fiend:80";

	private const string ModMonsterDefaults = "elder:90,floating head:200,contaminatedcentipede:10,snatcher:110,skinned crawler:140,dirty disturbed masked:50,gold disturbed masked:90,dark spring:40,baboon duck:10,infected bunker spider:70,arachnohead:85,corpse crawler:25,lavastingray:15,assimilated employee:80,assimilated growth:0,blackwhiteconfetbees:5,honey stinger:25,smile:90,droopman:100,gilded jester:120,runninglasso:80,toy face jester:80,duck:30,anti coil-head:200,nutslayer:180,kamikaziebugs:25,shy guy:100,observer:70";

	private const string HealthDefaults = "0:0.50,20:0.35,30:0.25,40:0.20,50:0.15,60:0.10,70:0.05,80:0";

	private const string DistanceDefaults = "1:1.00,2:0.75,3:0.50,5:0.25";

	private const string MonsterCountDefaults = "2:0.10,3:0.20,4:0.30";

	private static readonly Dictionary<string, string> PriceAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
	{
		{ "CosmicFlowerman", "Flowerman" },
		{ "Creaker", "Nutcracker" },
		{ "Key wielding Nutcracker", "Nutcracker" },
		{ "Disturbed masked", "Masked" },
		{ "Confetbees", "Butler Bees" },
		{ "Emma", "Girl" },
		{ "Hailey", "Girl" },
		{ "Ice Leviathan", "Earth Leviathan" },
		{ "AquaStingray", "Backwater Gunkfish" },
		{ "CryoStingray", "Backwater Gunkfish" },
		{ "OilStingray", "Backwater Gunkfish" },
		{ "Kamikazie Bug", "kamikazieBugs" },
		{ "SCP-096", "Shy guy" }
	};

	private Dictionary<string, decimal> monsters;

	private readonly Dictionary<MonsterSpecialState, decimal> specialRates = new Dictionary<MonsterSpecialState, decimal>();

	private SortedDictionary<int, decimal> health;

	private SortedDictionary<decimal, decimal> distanceBands;

	private SortedDictionary<decimal, decimal> monsterCountBands;

	private decimal distanceMaximum;

	private decimal monsterCountMaximum;

	private bool monsterCountEnabled;

	private bool distanceEnabled;

	public decimal ManeaterAdultValue { get; private set; }

	public decimal ManeaterTransformingValue { get; private set; }

	public decimal ManeaterScrapValue { get; private set; }

	public int ManeaterMaxScrapStacks { get; private set; }

	public decimal MonsterMultiplier { get; private set; }

	public decimal DefaultMonsterValue { get; private set; }

	public bool HealthEnabled { get; private set; }

	public bool PlayerCountEnabled { get; private set; }

	public bool EmoteEnabled { get; private set; }

	public decimal PlayerCountTwoBonus { get; private set; }

	public decimal PlayerCountThreeBonus { get; private set; }

	public decimal EmoteOneBonus { get; private set; }

	public decimal EmoteTwoBonus { get; private set; }

	public decimal EmoteThreeBonus { get; private set; }

	private RewardSettings()
	{
	}

	public static RewardSettings Parse(string config, Action<string> warn)
	{
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		string text = "";
		string[] array = (config ?? "").Replace("\r", "").Split(new char[1] { '\n' });
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim().TrimStart(new char[1] { '\ufeff' });
			if (text2.Length == 0 || text2[0] == '#' || text2[0] == ';')
			{
				continue;
			}
			if (text2.StartsWith("[") && text2.EndsWith("]"))
			{
				text = text2.Substring(1, text2.Length - 2).Trim();
				continue;
			}
			int num = text2.IndexOf('=');
			if (num <= 0)
			{
				warn?.Invoke("설정 " + (i + 1) + "행: 키 = 값 형식을 사용하세요.");
				continue;
			}
			string text3 = text + "." + text2.Substring(0, num).Trim();
			if (dictionary.ContainsKey(text3))
			{
				warn?.Invoke(text3 + ": 중복 항목에서 마지막 값을 사용합니다.");
			}
			dictionary[text3] = text2.Substring(num + 1).Trim();
		}
		ConfigReader configReader = new ConfigReader(dictionary, warn);
		RewardSettings rewardSettings = new RewardSettings();
		rewardSettings.monsters = ParseMonsters("flowerman:150,crawler:90,hoarding bug:10,centipede:20,bunker spider:60,puffer:10,jester:100,blob:5,girl:200,spring:50,nutcracker:80,masked:60,mouthdog:60,earth leviathan:150,forestgiant:60,baboon hawk:15,red locust bees:5,docile locust bees:0,manticoil:0,butler:20,butler bees:5,maneater:20,cavedweller:20,clay surgeon:100,tulip snake:5,old bird:100,bush wolf:40,giantkiwi:40,feiopar:30,backwater gunkfish:5,cadaver bloom:50,cadaver growth:0,peeper:2,locker:70,fiend:80,elder:90,floating head:200,contaminatedcentipede:10,snatcher:110,skinned crawler:140,dirty disturbed masked:50,gold disturbed masked:90,dark spring:40,baboon duck:10,infected bunker spider:70,arachnohead:85,corpse crawler:25,lavastingray:15,assimilated employee:80,assimilated growth:0,blackwhiteconfetbees:5,honey stinger:25,smile:90,droopman:100,gilded jester:120,runninglasso:80,toy face jester:80,duck:30,anti coil-head:200,nutslayer:180,kamikaziebugs:25,shy guy:100,observer:70");
		string text4 = configReader.Text("Monster.Values", "");
		Dictionary<string, decimal> dictionary2 = ParseMonsters(text4);
		if (dictionary2 == null)
		{
			configReader.Invalid("Monster.Values");
		}
		else
		{
			foreach (KeyValuePair<string, decimal> item in dictionary2)
			{
				rewardSettings.monsters[item.Key] = item.Value;
			}
		}
		rewardSettings.health = ParseHealth(configReader.Text("Health.Thresholds", "0:0.50,20:0.35,30:0.25,40:0.20,50:0.15,60:0.10,70:0.05,80:0"));
		if (rewardSettings.health == null)
		{
			configReader.Invalid("Health.Thresholds");
			rewardSettings.health = ParseHealth("0:0.50,20:0.35,30:0.25,40:0.20,50:0.15,60:0.10,70:0.05,80:0");
		}
		rewardSettings.DefaultMonsterValue = configReader.Number("Monster.DefaultValue", 10m, 0m, 2147483647m);
		rewardSettings.ManeaterAdultValue = configReader.Number("Monster.ManeaterAdultValue", 120m, 0m, 2147483647m);
		rewardSettings.ManeaterTransformingValue = configReader.Number("Monster.ManeaterTransformingValue", 90m, 0m, 2147483647m);
		rewardSettings.ManeaterScrapValue = configReader.Number("Monster.ManeaterScrapValue", 15m, 0m, 2147483647m);
		rewardSettings.ManeaterMaxScrapStacks = (int)configReader.Number("Monster.ManeaterMaxScrapStacks", 3m, 0m, 3m);
		rewardSettings.MonsterMultiplier = configReader.Number("Monster.Multiplier", 1m, 0m, 100m);
		rewardSettings.HealthEnabled = configReader.Flag("Health.Enabled", fallback: true);
		rewardSettings.PlayerCountEnabled = configReader.Flag("Players.CountEnabled", fallback: true);
		rewardSettings.EmoteEnabled = configReader.Flag("Players.EmoteEnabled", fallback: true);
		rewardSettings.PlayerCountTwoBonus = configReader.Number("Players.CountTwoBonus", 0.10m, 0m, 0.20m);
		rewardSettings.PlayerCountThreeBonus = configReader.Number("Players.CountThreeBonus", 0.20m, 0m, 0.20m);
		rewardSettings.EmoteOneBonus = configReader.Number("Players.EmoteOneBonus", 0.10m, 0m, 0.20m);
		rewardSettings.EmoteTwoBonus = configReader.Number("Players.EmoteTwoBonus", 0.15m, 0m, 0.20m);
		rewardSettings.EmoteThreeBonus = configReader.Number("Players.EmoteThreeBonus", 0.20m, 0m, 0.20m);
		rewardSettings.distanceBands = ReadBands(configReader, "Distance.Bands", counts: false, "1:1.00,2:0.75,3:0.50,5:0.25");
		rewardSettings.monsterCountBands = ReadBands(configReader, "MonsterCount.Bands", counts: true, "2:0.10,3:0.20,4:0.30");
		rewardSettings.distanceMaximum = configReader.Number("Distance.Maximum", 1m, 0m, 1m);
		rewardSettings.monsterCountMaximum = Math.Min(0.30m, configReader.Number("MonsterCount.Maximum", 0.30m, 0m, 100m));
		rewardSettings.monsterCountEnabled = configReader.Flag("MonsterCount.Enabled", fallback: true);
		rewardSettings.distanceEnabled = configReader.Flag("Distance.Enabled", fallback: true);
		string key = ((dictionary.ContainsKey("Special.GiantKiwiChasingPlayer") && !dictionary.ContainsKey("Special.GiantKiwiChasing")) ? "Special.GiantKiwiChasingPlayer" : "Special.GiantKiwiChasing");
		rewardSettings.specialRates.Add(MonsterSpecialState.JesterPoppedOut, configReader.Number("Special.JesterPoppedOut", 2.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.SnareFleaClinging, configReader.Number("Special.SnareFleaClinging", 0.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.ManeaterBabyHeld, configReader.Number("Special.ManeaterBabyHeld", 0.10m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.ManeaterAdultLeaping, configReader.Number("Special.ManeaterAdultLeaping", 1.00m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.BunkerSpiderWaitingOnWall, configReader.Number("Special.BunkerSpiderWaitingOnWall", 0.10m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.BunkerSpiderWrappingBody, configReader.Number("Special.BunkerSpiderWrappingBody", 0.75m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.CoilHeadStunned, configReader.Number("Special.CoilHeadStunned", 0.20m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.CoilHeadOnCooldown, configReader.Number("Special.CoilHeadOnCooldown", 0.20m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.ButlerKnifeDrawn, configReader.Number("Special.ButlerKnifeDrawn", 0.75m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.TulipSnakeLiftingPlayer, configReader.Number("Special.TulipSnakeLiftingPlayer", 1.00m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.KidnapperFoxDraggingPlayer, configReader.Number("Special.KidnapperFoxDraggingPlayer", 0.75m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.FeioparDroppingToAttack, configReader.Number("Special.FeioparDroppingToAttack", 1.00m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.CadaverBloomBursting, configReader.Number("Special.CadaverBloomBursting", 1.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.MaskedInfectingPlayer, configReader.Number("Special.MaskedInfectingPlayer", 0.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.GhostGirlChasingShooter, configReader.Number("Special.GhostGirlChasingShooter", 1.25m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.BrackenCarryingPlayer, configReader.Number("Special.BrackenCarryingPlayer", 2.00m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.BrackenExecutingAtNest, configReader.Number("Special.BrackenExecutingAtNest", 1.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.ForestKeeperEating, configReader.Number("Special.ForestKeeperEating", 1.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.GiantKiwiChasing, configReader.Number(key, 0.75m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.EyelessDogLunging, configReader.Number("Special.EyelessDogLunging", 0.75m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.HoardingBugCarrying, configReader.Number("Special.HoardingBugCarrying", 1.00m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.HoardingBugChasing, configReader.Number("Special.HoardingBugChasing", 0.40m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.NutcrackerInspecting, configReader.Number("Special.NutcrackerInspecting", 0.20m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.NutcrackerEngaging, configReader.Number("Special.NutcrackerEngaging", 0.50m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.LockerChasing, configReader.Number("Special.LockerChasing", 0.80m, 0m, 3m));
		rewardSettings.specialRates.Add(MonsterSpecialState.ShyGuyChasing, configReader.Number("Special.ShyGuyChasing", 3.00m, 0m, 3m));
		return rewardSettings;
	}

	public decimal SpecialBonus(MonsterSpecialState state)
	{
		if (state == MonsterSpecialState.HoardingBugCarryingAndChasing)
		{
			return Math.Max(specialRates[MonsterSpecialState.HoardingBugCarrying], specialRates[MonsterSpecialState.HoardingBugChasing]);
		}
		if (!specialRates.TryGetValue(state, out var value))
		{
			return 0m;
		}
		return value;
	}

	public string ResolveMonsterPriceName(string detectedName, string vanillaName)
	{
		if (detectedName == null)
		{
			return vanillaName;
		}
		string text = detectedName.Trim();
		string text2;
		if (!monsters.ContainsKey(text) && !PriceAliases.ContainsKey(text))
		{
			text2 = vanillaName;
			if (text2 == null)
			{
				return text;
			}
		}
		else
		{
			text2 = text;
		}
		return text2;
	}

	public decimal MonsterValue(string name)
	{
		if (name == null)
		{
			return DefaultMonsterValue;
		}
		name = name.Trim();
		if (monsters.TryGetValue(name, out var value))
		{
			return value;
		}
		if (!PriceAliases.TryGetValue(name, out var value2) || !monsters.TryGetValue(value2, out value))
		{
			return DefaultMonsterValue;
		}
		return value;
	}

	public decimal HealthBonus(int hitPoints)
	{
		decimal val = 0m;
		foreach (KeyValuePair<int, decimal> item in health)
		{
			if (item.Key <= Math.Max(0, hitPoints))
			{
				val = item.Value;
				continue;
			}
			break;
		}
		return Math.Min(val, 0.50m);
	}

	internal decimal DistanceBonus(double meters)
	{
		if (distanceEnabled)
		{
			foreach (KeyValuePair<decimal, decimal> distanceBand in distanceBands)
			{
				if (meters <= (double)distanceBand.Key)
				{
					return Math.Min(distanceBand.Value, distanceMaximum);
				}
			}
			return 0m;
		}
		return 0m;
	}

	internal decimal MonsterCountBonus(int count)
	{
		if (!monsterCountEnabled)
		{
			return 0m;
		}
		decimal val = 0m;
		foreach (KeyValuePair<decimal, decimal> monsterCountBand in monsterCountBands)
		{
			if (!((decimal)count < monsterCountBand.Key))
			{
				val = monsterCountBand.Value;
				continue;
			}
			break;
		}
		return Math.Min(val, monsterCountMaximum);
	}

	private static SortedDictionary<decimal, decimal> ReadBands(ConfigReader read, string key, bool counts, string defaults)
	{
		SortedDictionary<decimal, decimal> sortedDictionary = new SortedDictionary<decimal, decimal>();
		string text = read.Text(key, defaults);
		if (text.Length == 0)
		{
			return sortedDictionary;
		}
		string[] array = text.Split(new char[1] { ',' });
		foreach (string text2 in array)
		{
			string[] array2 = text2.Split(new char[1] { ':' });
			if (array2.Length != 2 || !TryNumber(array2[0], counts ? 2 : 0, 2147483647m, out var value) || (counts && value != decimal.Truncate(value)) || !TryNumber(array2[1], 0m, 100m, out var value2) || sortedDictionary.ContainsKey(value))
			{
				read.Invalid(key);
				return ReadBands(new ConfigReader(new Dictionary<string, string>(), null), key, counts, defaults);
			}
			sortedDictionary.Add(value, value2);
		}
		decimal? num = null;
		foreach (KeyValuePair<decimal, decimal> item in sortedDictionary)
		{
			if (num.HasValue && (counts ? (item.Value < num.Value) : (item.Value > num.Value)))
			{
				read.Invalid(key);
				return ReadBands(new ConfigReader(new Dictionary<string, string>(), null), key, counts, defaults);
			}
			num = item.Value;
		}
		return sortedDictionary;
	}

	private static Dictionary<string, decimal> ParseMonsters(string text)
	{
		Dictionary<string, decimal> dictionary = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase);
		if (text.Length == 0)
		{
			return dictionary;
		}
		string[] array = text.Split(new char[1] { ',' });
		foreach (string text2 in array)
		{
			string[] array2 = text2.Split(new char[1] { ':' });
			if (array2.Length != 2 || array2[0].Trim().Length == 0 || !TryNumber(array2[1], 0m, 2147483647m, out var value) || dictionary.ContainsKey(array2[0].Trim()))
			{
				return null;
			}
			dictionary.Add(array2[0].Trim(), value);
		}
		return dictionary;
	}

	private static SortedDictionary<int, decimal> ParseHealth(string text)
	{
		SortedDictionary<int, decimal> sortedDictionary = new SortedDictionary<int, decimal>();
		string[] array = text.Split(new char[1] { ',' });
		foreach (string text2 in array)
		{
			string[] array2 = text2.Split(new char[1] { ':' });
			if (array2.Length != 2 || !int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0 || !TryNumber(array2[1], 0m, 100m, out var value) || sortedDictionary.ContainsKey(result))
			{
				return null;
			}
			sortedDictionary.Add(result, value);
		}
		if (!sortedDictionary.ContainsKey(0))
		{
			return null;
		}
		return sortedDictionary;
	}

	private static bool TryNumber(string raw, decimal min, decimal max, out decimal value)
	{
		if (decimal.TryParse(raw, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value) && value >= min)
		{
			return value <= max;
		}
		return false;
	}
}
public struct CapturePosition
{
	private readonly double x;

	private readonly double y;

	private readonly double z;

	public CapturePosition(double x, double y, double z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}

	internal double SquaredDistanceTo(CapturePosition other)
	{
		double num = x - other.x;
		double num2 = y - other.y;
		double num3 = z - other.z;
		return num * num + num2 * num2 + num3 * num3;
	}
}
public enum MonsterSpecialState
{
	None,
	JesterPoppedOut,
	ForestKeeperEating,
	GiantKiwiChasing,
	EyelessDogLunging,
	HoardingBugCarrying,
	HoardingBugChasing,
	HoardingBugCarryingAndChasing,
	NutcrackerInspecting,
	NutcrackerEngaging,
	SnareFleaClinging,
	ManeaterBabyHeld,
	ManeaterTransforming,
	ManeaterAdultLeaping,
	BunkerSpiderWaitingOnWall,
	BunkerSpiderWrappingBody,
	CoilHeadStunned,
	CoilHeadOnCooldown,
	ButlerKnifeDrawn,
	TulipSnakeLiftingPlayer,
	KidnapperFoxDraggingPlayer,
	FeioparDroppingToAttack,
	CadaverBloomBursting,
	MaskedInfectingPlayer,
	GhostGirlChasingShooter,
	BrackenCarryingPlayer,
	BrackenExecutingAtNest,
	LockerChasing,
	ShyGuyChasing
}
public sealed class MonsterCapture
{
	public readonly ulong EntityId;

	public readonly string MonsterName;

	public readonly bool IsDormantOldBird;

	public readonly CapturePosition? Position;

	public readonly MonsterSpecialState SpecialState;

	public readonly int ManeaterBabyScrapEaten;

	public readonly bool IsAdultManeater;

	public readonly bool IsBurrowedLeviathan;

	public MonsterCapture(ulong entityId, string monsterName, CapturePosition? position = null, MonsterSpecialState specialState = MonsterSpecialState.None, bool isDormantOldBird = false, int maneaterBabyScrapEaten = 0, bool isAdultManeater = false, bool isBurrowedLeviathan = false)
	{
		EntityId = entityId;
		MonsterName = monsterName;
		Position = position;
		SpecialState = specialState;
		IsDormantOldBird = isDormantOldBird;
		ManeaterBabyScrapEaten = maneaterBabyScrapEaten;
		IsAdultManeater = isAdultManeater;
		IsBurrowedLeviathan = isBurrowedLeviathan;
	}
}
public sealed class PlayerCapture
{
	public readonly ulong PlayerId;

	public readonly int HitPoints;

	public readonly bool HasSpecialEmote;

	public readonly CapturePosition? Position;

	public PlayerCapture(ulong playerId, int hitPoints, bool hasSpecialEmote, CapturePosition? position = null)
	{
		PlayerId = playerId;
		HitPoints = hitPoints;
		HasSpecialEmote = hasSpecialEmote;
		Position = position;
	}
}
public sealed class PhotoReward
{
	public readonly IReadOnlyDictionary<ulong, int> Appraisals;

	public readonly decimal HealthBonus;

	public readonly decimal PlayerCountBonus;

	public readonly decimal EmoteBonus;

	public readonly decimal MonsterCountBonus;

	public readonly decimal CommonMultiplier;

	internal PhotoReward(Dictionary<ulong, int> appraisals, decimal healthBonus, decimal playerCountBonus, decimal emoteBonus, decimal monsterCountBonus, decimal commonMultiplier)
	{
		Appraisals = new ReadOnlyDictionary<ulong, int>(appraisals);
		HealthBonus = healthBonus;
		PlayerCountBonus = playerCountBonus;
		EmoteBonus = emoteBonus;
		MonsterCountBonus = monsterCountBonus;
		CommonMultiplier = commonMultiplier;
	}
}
public sealed class PhotoRewardCalculator
{
	private readonly RewardSettings settings;

	public PhotoRewardCalculator(RewardSettings settings)
	{
		if (settings == null)
		{
			throw new ArgumentNullException("settings");
		}
		this.settings = settings;
	}

	public PhotoReward Calculate(IEnumerable<MonsterCapture> monsters, IEnumerable<PlayerCapture> players, ulong? shooterId = null)
	{
		HashSet<ulong> hashSet = new HashSet<ulong>();
		List<CapturePosition> list = new List<CapturePosition>();
		int num = int.MaxValue;
		int num2 = 0;
		foreach (PlayerCapture player in players)
		{
			if (player.PlayerId != shooterId && hashSet.Add(player.PlayerId))
			{
				CapturePosition? position = player.Position;
				if (position.HasValue)
				{
					CapturePosition? position2 = player.Position;
					list.Add(position2.Value);
				}
				num = Math.Min(num, player.HitPoints);
				if (player.HasSpecialEmote)
				{
					num2++;
				}
			}
		}
		decimal num3 = ((settings.HealthEnabled && hashSet.Count > 0) ? settings.HealthBonus(num) : 0m);
		decimal num4 = 0m;
		if (settings.PlayerCountEnabled && hashSet.Count >= 2)
		{
			num4 = ((hashSet.Count == 2) ? settings.PlayerCountTwoBonus : settings.PlayerCountThreeBonus);
		}
		decimal num5 = 0m;
		if (settings.EmoteEnabled)
		{
			switch (Math.Min(3, num2))
			{
			case 1:
				num5 = settings.EmoteOneBonus;
				break;
			case 2:
				num5 = settings.EmoteTwoBonus;
				break;
			case 3:
				num5 = settings.EmoteThreeBonus;
				break;
			}
		}
		Dictionary<ulong, decimal> dictionary = new Dictionary<ulong, decimal>();
		foreach (MonsterCapture monster in monsters)
		{
			if (!monster.IsDormantOldBird && !dictionary.ContainsKey(monster.EntityId))
			{
				bool flag = monster.SpecialState == MonsterSpecialState.ManeaterTransforming;
				decimal num6 = settings.MonsterValue(monster.MonsterName);
				if (flag)
				{
					num6 = settings.ManeaterTransformingValue;
				}
				else if (monster.IsAdultManeater)
				{
					num6 = settings.ManeaterAdultValue;
				}
				else
				{
					num6 += (decimal)Math.Min(settings.ManeaterMaxScrapStacks, Math.Max(0, monster.ManeaterBabyScrapEaten)) * settings.ManeaterScrapValue;
				}
				if (monster.IsBurrowedLeviathan)
				{
					num6 = 0m;
				}
				decimal num7 = num6 * settings.MonsterMultiplier;
				if (!(num7 <= 0m))
				{
					decimal num8 = DistanceBonus(monster.Position, list);
					decimal num9 = settings.SpecialBonus(monster.SpecialState);
					dictionary.Add(monster.EntityId, num7 * (num8 + 1m) * (num9 + 1m));
				}
			}
		}
		decimal num10 = settings.MonsterCountBonus(dictionary.Count);
		decimal num11 = (num10 + 1m) * (num3 + 1m) * (num4 + 1m) * (num5 + 1m);
		Dictionary<ulong, int> dictionary2 = new Dictionary<ulong, int>();
		foreach (KeyValuePair<ulong, decimal> item in dictionary)
		{
			dictionary2.Add(item.Key, (int)decimal.Truncate(item.Value * num11));
		}
		return new PhotoReward(dictionary2, num3, num4, num5, num10, num11);
	}

	private decimal DistanceBonus(CapturePosition? monster, List<CapturePosition> players)
	{
		if (!monster.HasValue)
		{
			return 0m;
		}
		double num = double.PositiveInfinity;
		foreach (CapturePosition player in players)
		{
			double num2 = monster.Value.SquaredDistanceTo(player);
			if (num2 < num)
			{
				num = num2;
			}
		}
		if (!double.IsPositiveInfinity(num))
		{
			return settings.DistanceBonus(Math.Sqrt(num));
		}
		return 0m;
	}
}
public sealed class PayoutLedger
{
	public sealed class Payout
	{
		internal readonly Dictionary<ulong, int> Increases;

		public int Value { get; private set; }

		internal Payout(Dictionary<ulong, int> increases, int value)
		{
			Increases = increases;
			Value = value;
		}
	}

	private readonly object gate = new object();

	private readonly Dictionary<ulong, int> highest = new Dictionary<ulong, int>();

	private Payout pending;

	public Payout Prepare(IReadOnlyDictionary<ulong, int> appraisals)
	{
		lock (gate)
		{
			if (pending != null)
			{
				throw new InvalidOperationException("A photo is already being spawned.");
			}
			Dictionary<ulong, int> dictionary = new Dictionary<ulong, int>();
			int num = 0;
			foreach (KeyValuePair<ulong, int> appraisal in appraisals)
			{
				highest.TryGetValue(appraisal.Key, out var value);
				if (appraisal.Value > value)
				{
					num = checked(num + (appraisal.Value - value));
					dictionary.Add(appraisal.Key, appraisal.Value);
				}
			}
			pending = new Payout(dictionary, num);
			return pending;
		}
	}

	public bool Complete(Payout payout, bool spawned)
	{
		lock (gate)
		{
			if (payout == null || !object.ReferenceEquals(pending, payout))
			{
				return false;
			}
			if (spawned)
			{
				foreach (KeyValuePair<ulong, int> increase in payout.Increases)
				{
					highest[increase.Key] = increase.Value;
				}
			}
			pending = null;
			return spawned;
		}
	}

	public void Reset()
	{
		lock (gate)
		{
			highest.Clear();
			pending = null;
		}
	}
}