Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalSnapRewardsAddon v1.0.0
plugins/LethalSnapRewardsAddon/LethalSnapRewards.Preview.dll
Decompiled 25 minutes agousing 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 agousing 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; } } }