Decompiled source of Loop The Gungeon v1.1.2
LoopTheGungeon.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Alexandria.CharacterAPI; using Alexandria.DungeonAPI; using Alexandria.ItemAPI; using Alexandria.Misc; using Alexandria.NPCAPI; using BepInEx; using BepInEx.Logging; using Brave.BulletScript; using Dungeonator; using Gunfiguration; using HarmonyLib; using HutongGames.PlayMaker; using InControl; using LoopTheGungeon.Config; using LoopTheGungeon.Console; using LoopTheGungeon.Content; using LoopTheGungeon.Core; using LoopTheGungeon.Patches; using LoopTheGungeon.Util; using LoopTheGungeon.Visuals; using Microsoft.CodeAnalysis; using Pathfinding; using SGUI; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("LoopTheGungeon")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LoopTheGungeon")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d6d7a494-722e-4763-959b-c2d6b6a42b01")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } public static class HellRage { public class HellRageOnCombat : MonoBehaviour { private float RAGE_DURATION = 6f; private float _currentDamageMultiplier = 1.5f; private Color _currentFlatColor = new Color(0.5f, 0f, 0f, 0.75f); private PlayerController _owner; private Action _enteredCombatHandler; private Coroutine _activationCo; private Coroutine _rageCo; private StatModifier _damageMod; private float _rageTimeLeft; private bool _buffActive; public void Init(PlayerController owner) { _owner = owner; if (_enteredCombatHandler == null) { _enteredCombatHandler = OnEnteredCombat; } if ((Object)(object)_owner != (Object)null) { PlayerController owner2 = _owner; owner2.OnEnteredCombat = (Action)Delegate.Remove(owner2.OnEnteredCombat, _enteredCombatHandler); PlayerController owner3 = _owner; owner3.OnEnteredCombat = (Action)Delegate.Combine(owner3.OnEnteredCombat, _enteredCombatHandler); } } private void OnDestroy() { CleanupSubscriptions(); CleanupBuff(_owner); } private void CleanupSubscriptions() { if ((Object)(object)_owner != (Object)null && _enteredCombatHandler != null) { PlayerController owner = _owner; owner.OnEnteredCombat = (Action)Delegate.Remove(owner.OnEnteredCombat, _enteredCombatHandler); } _enteredCombatHandler = null; _owner = null; } private void OnEnteredCombat() { //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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_owner == (Object)null) { return; } int rageStacks = GetRageStacks(_owner); if (rageStacks > 0) { _currentDamageMultiplier = 1f + 0.5f * (float)rageStacks; if (rageStacks > 3) { _currentFlatColor = new Color(0.388f, 0.067f, 0.729f, 0.75f); } else { _currentFlatColor = new Color(0.5f, 0f, 0f, 0.75f); } _rageTimeLeft = RAGE_DURATION; if (_rageCo == null) { _rageCo = ((MonoBehaviour)_owner).StartCoroutine(RageCR(_owner)); } } } private IEnumerator RageCR(PlayerController player) { yield return (object)new WaitForSeconds(0.5f); if (!_buffActive) { _buffActive = true; _damageMod = new StatModifier { amount = _currentDamageMultiplier, modifyType = (ModifyMethod)1, statToBoost = (StatType)5 }; player.ownerlessStatModifiers.Add(_damageMod); player.stats.RecalculateStats(player, false, false); if ((Object)(object)((GameActor)player).CurrentGun != (Object)null) { ((GameActor)player).CurrentGun.ForceImmediateReload(false); } } float particleCounter = 0f; while (_rageTimeLeft > 0f) { _rageTimeLeft -= BraveTime.DeltaTime; float num = ((_rageTimeLeft <= 1f) ? (1f - _rageTimeLeft) : 0f); player.baseFlatColorOverride = Vector3Extensions.WithAlpha(_currentFlatColor, Mathf.Lerp(_currentFlatColor.a, 0f, num)); if ((int)GameManager.Options.ShaderQuality != 0 && (int)GameManager.Options.ShaderQuality != 3 && Object.op_Implicit((Object)(object)player) && player.IsVisible && !((GameActor)player).IsFalling) { particleCounter += BraveTime.DeltaTime * 40f; if (particleCounter > 1f) { int num2 = Mathf.FloorToInt(particleCounter); particleCounter %= 1f; GlobalSparksDoer.DoRandomParticleBurst(num2, Vector2Extensions.ToVector3ZisY(((BraveBehaviour)player).sprite.WorldBottomLeft, 0f), Vector2Extensions.ToVector3ZisY(((BraveBehaviour)player).sprite.WorldTopRight, 0f), Vector3.up, 90f, 0.5f, (float?)null, (float?)null, (Color?)null, (SparksType)1); } } yield return null; if ((Object)(object)player == (Object)null) { break; } } CleanupBuff(player); _rageCo = null; } public void PlayActivationFx(float seconds = 3f, int burstCount = 40) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_owner == (Object)null)) { if (GetRageStacks(_owner) > 3) { _currentFlatColor = new Color(0.388f, 0.067f, 0.729f, 0.75f); } else { _currentFlatColor = new Color(0.5f, 0f, 0f, 0.75f); } if (_activationCo != null) { ((MonoBehaviour)_owner).StopCoroutine(_activationCo); } _activationCo = ((MonoBehaviour)_owner).StartCoroutine(ActivationFxCR(_owner, seconds, burstCount)); } } private IEnumerator ActivationFxCR(PlayerController player, float seconds, int burstCount) { if (!Object.op_Implicit((Object)(object)player)) { yield break; } try { if ((int)GameManager.Options.ShaderQuality != 0 && (int)GameManager.Options.ShaderQuality != 3 && player.IsVisible && !((GameActor)player).IsFalling && (Object)(object)((BraveBehaviour)player).sprite != (Object)null) { GlobalSparksDoer.DoRandomParticleBurst(Mathf.Max(1, burstCount), Vector2Extensions.ToVector3ZisY(((BraveBehaviour)player).sprite.WorldBottomLeft, 0f), Vector2Extensions.ToVector3ZisY(((BraveBehaviour)player).sprite.WorldTopRight, 0f), Vector3.up, 90f, 0.6f, (float?)null, (float?)null, (Color?)null, (SparksType)1); } } catch { } float t = 0f; float dur = Mathf.Max(0.05f, seconds); while (t < dur && Object.op_Implicit((Object)(object)player) && _rageCo == null) { t += BraveTime.DeltaTime; float num = 1f - Mathf.Clamp01(t / dur); player.baseFlatColorOverride = Vector3Extensions.WithAlpha(_currentFlatColor, _currentFlatColor.a * num); yield return null; } if (Object.op_Implicit((Object)(object)player)) { player.baseFlatColorOverride = Color.clear; } _activationCo = null; } private void CleanupBuff(PlayerController player) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null) { player.baseFlatColorOverride = Color.clear; if (_buffActive && _damageMod != null) { player.ownerlessStatModifiers.Remove(_damageMod); player.stats.RecalculateStats(player, false, false); } } _damageMod = null; _buffActive = false; _rageTimeLeft = 0f; } } public static int GetRageStacks(PlayerController p) { if (!Object.op_Implicit((Object)(object)p)) { return 0; } MasteryShrineInteractable.ShrineRageMarker component = ((Component)p).GetComponent<MasteryShrineInteractable.ShrineRageMarker>(); if (!Object.op_Implicit((Object)(object)component)) { return 0; } return Mathf.Max(0, component.Stacks); } public static void ResetRageStacksForAllPlayers() { GameManager instance = GameManager.Instance; PlayerController[] array = ((instance != null) ? instance.AllPlayers : null); if (array == null) { return; } foreach (PlayerController val in array) { if (!Object.op_Implicit((Object)(object)val)) { continue; } try { MasteryShrineInteractable.ShrineRageMarker component = ((Component)val).GetComponent<MasteryShrineInteractable.ShrineRageMarker>(); if ((Object)(object)component != (Object)null) { component.Stacks = 0; } } catch { } try { HellRageOnCombat component2 = ((Component)val).GetComponent<HellRageOnCombat>(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } } catch { } } } } namespace Mod.Patches { [HarmonyPatch(typeof(InfinilichDeathController), "HandleClockhair")] public static class Patch_InfinilichDeathController_HandleClockhair { private static bool _loopShotTriggeredThisClockhair; private static Vector2 _camVel; private const float MAX_CAM_DRIFT_FROM_LICH = 25f; private const float EDGE_UV = 0.1f; private const float PAN_SPEED_MAX = 6f; private const float PAN_ACCEL = 10f; [HarmonyPrefix] private static bool Prefix(InfinilichDeathController __instance, PlayerController interactor, ref IEnumerator __result) { __result = CustomHandleClockhair(__instance, interactor); return false; } private static IEnumerator CustomHandleClockhair(InfinilichDeathController self, PlayerController interactor) { _loopShotTriggeredThisClockhair = false; _camVel = Vector2.zero; try { GameManager.Instance.PrimaryPlayer.SetInputOverride("past"); if ((int)GameManager.Instance.CurrentGameType == 1 && Object.op_Implicit((Object)(object)GameManager.Instance.SecondaryPlayer)) { GameManager.Instance.SecondaryPlayer.SetInputOverride("past"); } GameUIRoot.Instance.ToggleLowerPanels(false, false, string.Empty); GameUIRoot.Instance.HideCoreUI(string.Empty); CameraController camera = GameManager.Instance.MainCameraController; Vector2 val = Vector2.op_Implicit(camera.OverridePosition); Vector2 desiredCamCenter = ((BraveBehaviour)((BraveBehaviour)self).aiAnimator).sprite.WorldCenter; camera.SetManualControl(true, true); _camVel = Vector2.zero; if (Vector2.Distance(val, desiredCamCenter) > 2.5f) { camera.OverridePosition = Vector2.op_Implicit(desiredCamCenter); } Transform clockhairTransform = ((GameObject)Object.Instantiate(BraveResources.Load("Clockhair", ".prefab"))).transform; ClockhairController clockhair = ((Component)clockhairTransform).GetComponent<ClockhairController>(); float elapsed = 0f; float duration = clockhair.ClockhairInDuration; Vector3 clockhairTargetPosition = Vector2.op_Implicit(((GameActor)GameManager.Instance.PrimaryPlayer).CenterPosition); Vector3 clockhairStartPosition = clockhairTargetPosition + new Vector3(-20f, 5f, 0f); ((BraveBehaviour)clockhair).renderer.enabled = true; ((BraveBehaviour)clockhair).spriteAnimator.alwaysUpdateOffscreen = true; ((BraveBehaviour)clockhair).spriteAnimator.Play("clockhair_intro"); clockhair.hourAnimator.Play("hour_hand_intro"); clockhair.minuteAnimator.Play("minute_hand_intro"); clockhair.secondAnimator.Play("second_hand_intro"); while (elapsed < duration) { if (GameManager.INVARIANT_DELTA_TIME == 0f) { elapsed += 0.05f; } elapsed += GameManager.INVARIANT_DELTA_TIME; float num = elapsed / duration; float num2 = Mathf.SmoothStep(0f, 1f, num); clockhairTargetPosition = Vector2.op_Implicit(GetTargetClockhairPosition(self, Vector3Extensions.XY(clockhairTargetPosition))); Vector3 val2 = Vector3.Slerp(clockhairStartPosition, clockhairTargetPosition, num2); clockhairTransform.position = Vector3Extensions.WithZ(val2, 0f); UpdateCameraDuringLichClockhair(camera, desiredCamCenter); UpdateEyes(self, Vector3Extensions.XY(clockhairTransform.position), isInDanger: false); if (num > 0.5f) { ((BraveBehaviour)clockhair).renderer.enabled = true; } if (num > 0.75f) { ((Component)clockhair.hourAnimator).GetComponent<Renderer>().enabled = true; ((Component)clockhair.minuteAnimator).GetComponent<Renderer>().enabled = true; ((Component)clockhair.secondAnimator).GetComponent<Renderer>().enabled = true; } ((BraveBehaviour)clockhair).sprite.UpdateZDepth(); yield return null; } clockhair.SetMotionType(1f); BraveInput currentInput = BraveInput.GetInstanceForPlayer(GameManager.Instance.PrimaryPlayer.PlayerIDX); float shotTargetTime = 0f; float holdDuration = 3f; Vector3 lastScreenShakeAmount = Vector3.zero; Vector3 lastJitterAmount = Vector3.zero; bool targetingPlayer; PlayerController targetedPlayer; while (true) { Transform transform = ((BraveBehaviour)clockhair).transform; transform.position -= lastJitterAmount; ((BraveBehaviour)clockhair).transform.position = Vector2.op_Implicit(GetTargetClockhairPosition(self, Vector3Extensions.XY(((BraveBehaviour)clockhair).transform.position))); ((BraveBehaviour)clockhair).sprite.UpdateZDepth(); bool num3 = CheckLichTarget(self, clockhairTransform); targetingPlayer = false; targetedPlayer = null; if (Object.op_Implicit((Object)(object)GameManager.Instance.PrimaryPlayer) && Object.op_Implicit((Object)(object)((BraveBehaviour)GameManager.Instance.PrimaryPlayer).healthHaver) && ((BraveBehaviour)GameManager.Instance.PrimaryPlayer).healthHaver.IsAlive && CheckPlayerTarget(GameManager.Instance.PrimaryPlayer, clockhairTransform)) { targetingPlayer = true; targetedPlayer = GameManager.Instance.PrimaryPlayer; } if (!targetingPlayer && (int)GameManager.Instance.CurrentGameType == 1 && Object.op_Implicit((Object)(object)GameManager.Instance.SecondaryPlayer) && Object.op_Implicit((Object)(object)((BraveBehaviour)GameManager.Instance.SecondaryPlayer).healthHaver) && ((BraveBehaviour)GameManager.Instance.SecondaryPlayer).healthHaver.IsAlive && CheckPlayerTarget(GameManager.Instance.SecondaryPlayer, clockhairTransform)) { targetingPlayer = true; targetedPlayer = GameManager.Instance.SecondaryPlayer; } bool flag = num3 || targetingPlayer; clockhair.SetMotionType(flag ? (-10f) : 1f); bool flag2 = ((OneAxisInputControl)currentInput.ActiveActions.ShootAction).IsPressed || ((OneAxisInputControl)currentInput.ActiveActions.InteractAction).IsPressed; shotTargetTime = ((flag2 && flag) ? (shotTargetTime + BraveTime.DeltaTime) : Mathf.Max(0f, shotTargetTime - BraveTime.DeltaTime * 3f)); UpdateCameraDuringLichClockhair(camera, desiredCamCenter); UpdateEyes(self, Vector3Extensions.XY(((BraveBehaviour)clockhair).transform.position), flag2); if ((((OneAxisInputControl)currentInput.ActiveActions.ShootAction).WasReleased || ((OneAxisInputControl)currentInput.ActiveActions.InteractAction).WasReleased) && flag && shotTargetTime > holdDuration && !GameManager.Instance.IsPaused) { break; } if (shotTargetTime > 0f) { lastScreenShakeAmount = camera.DoFrameScreenShake(Mathf.Lerp(0f, 0.5f, shotTargetTime / holdDuration), Mathf.Lerp(3f, 8f, shotTargetTime / holdDuration), Vector2.op_Implicit(Vector3.right), lastScreenShakeAmount, Time.realtimeSinceStartup); float num4 = Mathf.Lerp(0f, 0.35f, shotTargetTime / holdDuration); float num5 = 0.5f; float num6 = Mathf.Lerp(4f, 7f, shotTargetTime / holdDuration); clockhair.UpdateDistortion(num4, num5, num6); float num7 = Mathf.Lerp(2f, 0.25f, shotTargetTime / holdDuration); clockhair.UpdateDesat(true, num7); shotTargetTime = Mathf.Min(holdDuration + 0.25f, shotTargetTime + BraveTime.DeltaTime); float num8 = Mathf.Lerp(0f, 0.5f, (shotTargetTime - 1f) / (holdDuration - 1f)); Vector3 val3 = Vector2Extensions.ToVector3ZUp(Random.insideUnitCircle * num8, 0f); BraveInput.DoSustainedScreenShakeVibration(shotTargetTime / holdDuration * 0.8f); Transform transform2 = ((BraveBehaviour)clockhair).transform; transform2.position += val3; lastJitterAmount = val3; clockhair.SetMotionType(Mathf.Lerp(-10f, -2400f, shotTargetTime / holdDuration)); } else { lastJitterAmount = Vector3.zero; camera.ClearFrameScreenShake(lastScreenShakeAmount); lastScreenShakeAmount = Vector3.zero; clockhair.UpdateDistortion(0f, 0f, 0f); clockhair.UpdateDesat(false, 0f); shotTargetTime = 0f; BraveInput.DoSustainedScreenShakeVibration(0f); } yield return null; } camera.ClearFrameScreenShake(lastScreenShakeAmount); BraveInput.DoSustainedScreenShakeVibration(0f); BraveInput.DoVibrationForAllPlayers((Time)10, (Strength)30); ((MonoBehaviour)clockhair).StartCoroutine(clockhair.WipeoutDistortionAndFade(0.5f)); GameObjectExtensions.SetLayerRecursively(((Component)clockhair).gameObject, LayerMask.NameToLayer("Unoccluded")); Pixelator.Instance.FadeToBlack(0.2f, true, 0.2f); Pixelator.Instance.DoRenderGBuffer = false; Pixelator.Instance.LerpToLetterbox(0.45f, 0.45f); ((BraveBehaviour)clockhair).spriteAnimator.PlayAndDisableRenderer("clockhair_fire"); ((Component)clockhair.hourAnimator).GetComponent<Renderer>().enabled = false; ((Component)clockhair.minuteAnimator).GetComponent<Renderer>().enabled = false; ((Component)clockhair.secondAnimator).GetComponent<Renderer>().enabled = false; yield return null; if (!targetingPlayer || !((Object)(object)targetedPlayer != (Object)null) || _loopShotTriggeredThisClockhair) { yield break; } _loopShotTriggeredThisClockhair = true; LoopManager.PendingLichRewards = true; try { clockhair.UpdateDesat(false, 0f); } catch { } try { clockhair.UpdateDistortion(0f, 0f, 0f); } catch { } try { GameManager.Instance.PrimaryPlayer.ClearInputOverride("past"); } catch { } try { if (Object.op_Implicit((Object)(object)GameManager.Instance.SecondaryPlayer)) { GameManager.Instance.SecondaryPlayer.ClearInputOverride("past"); } } catch { } try { ((MonoBehaviour)GameManager.Instance).StopAllCoroutines(); } catch { } try { LoopManager.LoadingScreenState.UseCustomLoadScreenOnce = true; } catch { } try { TimeTubeTransition.StartLoopTimeTubeThenLoop((MonoBehaviour)(object)GameManager.Instance); } catch { } } finally { _camVel = Vector2.zero; } } private static void UpdateCameraDuringLichClockhair(CameraController cam, Vector2 lichAnchor) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)cam == (Object)null) && !((Object)(object)cam.Camera == (Object)null)) { Vector3 val = cam.Camera.ScreenToViewportPoint(Input.mousePosition); float num = 0f; if (val.x < 0.1f) { num = (val.x - 0.1f) / 0.1f; } else if (val.x > 0.9f) { num = (val.x - 0.9f) / 0.1f; } float num2 = 0f; if (val.y < 0.1f) { num2 = (val.y - 0.1f) / 0.1f; } else if (val.y > 0.9f) { num2 = (val.y - 0.9f) / 0.1f; } Vector2 val2 = new Vector2(num, num2) * 6f; _camVel = Vector2.MoveTowards(_camVel, val2, BraveTime.DeltaTime * 10f); Vector2 val3 = Vector2.op_Implicit(cam.OverridePosition) + _camVel * BraveTime.DeltaTime; float num3 = Vector2.Distance(val3, lichAnchor); if (num3 > 25f && num3 > 0.001f) { val3 = lichAnchor + (val3 - lichAnchor) * (25f / num3); } cam.OverridePosition = Vector2.op_Implicit(val3); } } private static bool CheckPlayerTarget(PlayerController target, Transform clockhairTransform) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) return Vector2.Distance(Vector3Extensions.XY(clockhairTransform.position) + new Vector2(-0.375f, 0.25f), ((GameActor)target).CenterPosition) < 0.625f; } private static bool CheckLichTarget(InfinilichDeathController self, Transform clockhairTransform) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) try { return Vector2.Distance(Vector3Extensions.XY(clockhairTransform.position) + new Vector2(-0.375f, 0.25f), ((GameActor)((BraveBehaviour)self).aiActor).CenterPosition + new Vector2(0f, -1.25f)) < 0.875f; } catch { return false; } } private static Vector2 GetTargetClockhairPosition(InfinilichDeathController self, Vector2 currentClockhairPosition) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) BraveInput instanceForPlayer = BraveInput.GetInstanceForPlayer(GameManager.Instance.PrimaryPlayer.PlayerIDX); if (!instanceForPlayer.IsKeyboardAndMouse(false)) { Vector2 val = currentClockhairPosition + ((TwoAxisInputControl)instanceForPlayer.ActiveActions.Aim).Vector * 6f * BraveTime.DeltaTime; val = Vector2.Max(GameManager.Instance.MainCameraController.MinVisiblePoint, val); return Vector2.Min(GameManager.Instance.MainCameraController.MaxVisiblePoint, val); } Vector2 val2 = Vector3Extensions.XY(GameManager.Instance.MainCameraController.Camera.ScreenToWorldPoint(Input.mousePosition)) + new Vector2(0.375f, -0.25f); val2 = Vector2.Max(GameManager.Instance.MainCameraController.MinVisiblePoint, val2); return Vector2.Min(GameManager.Instance.MainCameraController.MaxVisiblePoint, val2); } private static void UpdateEyes(InfinilichDeathController self, Vector2 clockhairPosition, bool isInDanger) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) try { AccessTools.Method(typeof(InfinilichDeathController), "UpdateEyes", (Type[])null, (Type[])null)?.Invoke(self, new object[2] { clockhairPosition, isInDanger }); } catch { } } } [HarmonyPatch(typeof(LichIntroDoer), "PlayerWalkedIn")] public static class Patch_Lich_Start_TrackFlawless { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static OnDamagedEvent <>9__0_0; internal void <Postfix>b__0_0(float resultValue, float maxValue, CoreDamageTypes damageTypes, DamageCategory damageCategory, Vector2 damageDirection) { try { LoopManager.LastLichWasFlawless = false; } catch { } } } [HarmonyPostfix] private static void Postfix() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown try { LoopManager.LastLichWasFlawless = true; GameManager instance = GameManager.Instance; if (((instance != null) ? instance.AllPlayers : null) == null) { return; } for (int i = 0; i < instance.AllPlayers.Length; i++) { PlayerController val = instance.AllPlayers[i]; if (!Object.op_Implicit((Object)(object)val) || (Object)(object)((BraveBehaviour)val).healthHaver == (Object)null) { continue; } HealthHaver healthHaver = ((BraveBehaviour)val).healthHaver; object obj = <>c.<>9__0_0; if (obj == null) { OnDamagedEvent val2 = delegate { try { LoopManager.LastLichWasFlawless = false; } catch { } }; <>c.<>9__0_0 = val2; obj = (object)val2; } healthHaver.OnDamaged += (OnDamagedEvent)obj; } } catch { } } } } namespace LoopTheGungeon { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("milz.etg.loopthegungeon", "Loop The Gungeon", "1.1.2")] public class Plugin : BaseUnityPlugin { public const string GUID = "milz.etg.loopthegungeon"; public const string NAME = "Loop The Gungeon"; public const string VERSION = "1.1.2"; public static Plugin Instance; public static Harmony HarmonyInstance; public static int GlobalLoopOffset = -1; private static bool _commandsRegistered; private bool _patched; private bool _hooksInstalled; private SceneHandler _sceneHandler; private SaveHandler _saveHandler; private void Awake() { GlobalLoopOffset = -1; Instance = this; _saveHandler = new SaveHandler(); _sceneHandler = new SceneHandler(_saveHandler); try { InstallHooksOnce(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] Hook install failed: {1}", "Loop The Gungeon", arg)); } try { DataStorage.Init(); _saveHandler.TryRestorePendingResumeFromDisk(); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] Config init failed: {1}", "Loop The Gungeon", arg2)); } try { TryApplyHarmonyPatches(); } catch (Exception arg3) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] Harmony patching failed: {1}", "Loop The Gungeon", arg3)); } } private void InstallHooksOnce() { if (_hooksInstalled) { return; } _hooksInstalled = true; try { SceneManager.sceneLoaded += _sceneHandler.OnSceneLoaded; } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] sceneLoaded hook failed: {1}", "Loop The Gungeon", arg)); } try { DungeonHooks.OnPostDungeonGeneration += LoopManager.OnPostDungeonGeneration; } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] OnPostDungeonGeneration LoopManager hook failed: {1}", "Loop The Gungeon", arg2)); } try { DungeonHooks.OnPostDungeonGeneration += ForgeHandler.OnPostDungeonGeneration; } catch (Exception arg3) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] OnPostDungeonGeneration ForgeHandler hook failed: {1}", "Loop The Gungeon", arg3)); } try { DungeonHooks.OnPostDungeonGeneration += MasteryShrine.OnPostDungeonGeneration; } catch (Exception arg4) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] OnPostDungeonGeneration MasteryShrine hook failed: {1}", "Loop The Gungeon", arg4)); } } public void Start() { ETGModMainBehaviour.WaitForGameManagerStart((Action<GameManager>)GMStart); } private void GMStart(GameManager g) { if ((Object)(object)g == (Object)null) { return; } try { GunfigManager.Init(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] Gunfig init failed: {1}", "Loop The Gungeon", arg)); } try { MasterRoundHell.Init(); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] MasterRoundHell.Init failed: {1}", "Loop The Gungeon", arg2)); } IntroWave.LogIntro("Loop The Gungeon started LOoOoping!"); if (!LoopManager.IsResumeFlow()) { LoopManager.ResetNoticeEmitted = false; } if (_commandsRegistered) { return; } _commandsRegistered = true; try { ConsoleCommands.RegisterCommands(); } catch (Exception arg3) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] RegisterDebugCommands failed: {1}", "Loop The Gungeon", arg3)); } try { ETGModConsole.Commands.AddGroup("loop", (Action<string[]>)delegate { }); } catch (Exception arg4) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] AddGroup(loop) failed: {1}", "Loop The Gungeon", arg4)); } } private void TryApplyHarmonyPatches() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown if (_patched) { return; } try { HarmonyInstance = new Harmony("milz.etg.loopthegungeon"); Harmony harmonyInstance = HarmonyInstance; MethodInfo method = typeof(GameManager).GetMethod("DelayedLoadCharacterSelect", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)method == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"[Loop The Gungeon] Patch FAIL: GameManager.DelayedLoadCharacterSelect not found."); } else { harmonyInstance.Patch((MethodBase)method, new HarmonyMethod(typeof(LoopManager), "SaveAndQuitBoundary", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } harmonyInstance.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony patching OK."); _patched = true; } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("[{0}] Harmony patching FAILED: {1}", "Loop The Gungeon", arg)); } } private void OnDestroy() { try { _saveHandler?.StopCoroutines(); } catch { } try { _sceneHandler?.StopCoroutines(); } catch { } if (_hooksInstalled) { _hooksInstalled = false; if (_sceneHandler != null) { SceneManager.sceneLoaded -= _sceneHandler.OnSceneLoaded; } DungeonHooks.OnPostDungeonGeneration -= LoopManager.OnPostDungeonGeneration; DungeonHooks.OnPostDungeonGeneration -= MasteryShrine.OnPostDungeonGeneration; } if (Instance == this) { Instance = null; } try { ProjectileSpeedScaler.RestoreBaselineNow(); } catch { } try { ButtonSpritesOverride.ClearRuntimeCaches(destroyUnityObjects: true); } catch { } try { LoopManager.ResetMasteryShrineRuntime(); ShrineSpritesOverride.TryResetMasteryShrineVisuals(); ShrineSpritesOverride.ClearRuntimeCaches(destroyUnityObjects: true); } catch { } } internal static void StopAndNull(ref Coroutine c) { if (c != null && (Object)(object)Instance != (Object)null) { try { ((MonoBehaviour)Instance).StopCoroutine(c); } catch { } c = null; } } internal static Coroutine StartGlobalCoroutine(IEnumerator e) { if ((Object)(object)Instance == (Object)null) { return null; } try { return ((MonoBehaviour)Instance).StartCoroutine(e); } catch { return null; } } internal void ClearResumeAnnouncement() { try { _saveHandler?.ClearResumeAnnouncement(); } catch { } } internal void ArmResumeAnnouncementIfNeeded() { try { _saveHandler?.ArmResumeAnnouncementIfNeeded(); } catch { } } public static bool ShouldSuppressResumeArming() { try { return SaveHandler.ShouldSuppressResumeArming(); } catch { return false; } } } internal static class GunfigManager { private class ScalingPresetDefinition { internal readonly string Name; internal readonly string Hp; internal readonly string AtkSpeed; internal readonly string MoveSpeed; internal readonly string ProjSpeed; internal readonly string Enemies; internal ScalingPresetDefinition(string name, string hp, string atkSpeed, string moveSpeed, string projSpeed, string enemies) { Name = name; Hp = hp; AtkSpeed = atkSpeed; MoveSpeed = moveSpeed; ProjSpeed = projSpeed; Enemies = enemies; } } internal static Gunfig gunfig = null; internal static bool _initialized; private static readonly Color Header = new Color(0f, 1f, 0.702f); private const string ObsoleteConfigFileName = "milz.loopthegungeon.cfg"; private static readonly List<string> DefaultEnabled = new List<string> { "Yeah", "Nah" }; private static readonly List<string> DefaultDisabled = new List<string> { "Nah", "Yeah" }; internal const string ScalingPreset = "Scaling Preset"; private const string CustomScalingPreset = "Custom"; private const string GameBreakingScalingPreset = "Game Breaking"; private static bool _applyingScalingPreset; internal const string DPS = "Break DPS cap"; internal const string LOTJ = "Banish Lord of the Jammed"; internal const string TAX = "Pay Loop tax"; internal const string HpPerLoop = "Health"; internal const string AtkSpeedPerLoop = "Attack Speed"; internal const string MoveSpeedPerLoop = "Movement Speed"; internal const string ProjSpeedPerLoop = "Projectile Speed"; internal const string EnemiesPerLoop = "Room Wave Enemies"; internal const string ShopPricingPerLoop = "Shop Pricing"; internal const string SpecialPricingPerLoop = "Special NPC Pricing"; private static readonly List<string> PercentageOptions = new List<string> { "+0%", "+5%", "+10%", "+15%", "+20%", "+25%", "+30%", "+35%", "+40%", "+45%", "+50%", "+55%", "+60%", "+65%", "+70%", "+75%", "+80%", "+85%", "+90%", "+95%", "+100%", "+150%", "+200%", "+250%", "+300%" }; private static readonly List<string> NumberOptions = new List<string> { "+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10", "+15", "+20", "+30" }; private static readonly List<ScalingPresetDefinition> ScalingPresetDefinitions = new List<ScalingPresetDefinition> { new ScalingPresetDefinition("Off", "+0%", "+0%", "+0%", "+0%", "+0"), new ScalingPresetDefinition("Easy", "+10%", "+5%", "+5%", "+5%", "+1"), new ScalingPresetDefinition("Normal", "+20%", "+15%", "+10%", "+10%", "+2"), new ScalingPresetDefinition("Hard", "+40%", "+30%", "+20%", "+20%", "+3"), new ScalingPresetDefinition("Brutal", "+70%", "+50%", "+30%", "+30%", "+5"), new ScalingPresetDefinition("Impossible", "+100%", "+75%", "+50%", "+50%", "+10") }; private static readonly List<string> ScalingPresets = ScalingPresetDefinitions.Select((ScalingPresetDefinition preset) => preset.Name).Concat(new string[2] { "Custom", "Game Breaking" }).ToList(); internal static void Init() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _initialized = true; RemoveObsoleteConfig(); gunfig = Gunfig.Get(GunfigHelpers.WithColor("Loop The Gungeon", Header)); BuildMenu(); } } private static void RemoveObsoleteConfig() { try { string path = Path.Combine(Paths.ConfigPath, "milz.loopthegungeon.cfg"); if (File.Exists(path)) { File.Delete(path); Debug.Log((object)"[Loop The Gungeon] Removed obsolete configuration file: milz.loopthegungeon.cfg"); } } catch (Exception arg) { Debug.LogWarning((object)("[Loop The Gungeon] Could not remove obsolete configuration file. " + $"It can be safely deleted manually. Error: {arg}")); } } internal static void BuildMenu() { BuildGameplayMenu(); BuildEnemyMenu(); BuildEconomyMenu(); gunfig.AddLabel(GunfigHelpers.Gray(" . . . *")); gunfig.AddLabel(GunfigHelpers.Gray(" * . . * .")); gunfig.AddLabel(GunfigHelpers.Gray(" . * . * . .")); gunfig.AddLabel(GunfigHelpers.Gray(" . . . . .")); gunfig.AddLabel(GunfigHelpers.Gray(". * . . *")); gunfig.AddLabel(GunfigHelpers.Gray("* hi! . . * . .")); gunfig.AddLabel(GunfigHelpers.Gray("r _-_ \\|/")); gunfig.AddLabel(GunfigHelpers.Gray("(°°) (°o°) (^^)(°°) \\(°o°)/")); } private static void BuildGameplayMenu() { Gunfig obj = gunfig.AddSubMenu("Gameplay"); obj.ToggleDPS("Break DPS cap", "Removes the DPS cap, so your build can really shine."); obj.ToggleLOTJ("Banish Lord of the Jammed", "Removes the Lord of the Jammed, embraces the curse."); obj.ToggleTAX("Pay Loop tax", "Removes all casings every loop, for \"balance\"."); } private static void BuildEnemyMenu() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) Gunfig scaling = gunfig.AddSubMenu("Enemy Scaling"); scaling.AddScrollBox("Scaling Preset", GunfigHelpers.WithDefault(ScalingPresets, "Normal"), (string)null, (Action<string, string>)null, Enumerable.Repeat("Applies a preset to all enemy scaling modifiers.", ScalingPresets.Count).ToList(), (Update)1); scaling.SetSelectableValues("Scaling Preset", ScalingPresetDefinitions.Select((ScalingPresetDefinition preset) => preset.Name).ToList()); Gunfig obj = gunfig; obj.OnPendingValueChanged = (Action<string, string>)Delegate.Combine(obj.OnPendingValueChanged, (Action<string, string>)delegate(string key, string value) { if (key == "Scaling Preset") { ApplyScalingPreset(scaling, value); } else { UpdateScalingPreset(scaling); } }); scaling.EnemyPercentageList("Health", "Enemy health growth per loop (compounded).", Color.green, "+20%"); scaling.EnemyPercentageList("Attack Speed", "Enemy attack speed increase per loop (additive).", Color.red, "+20%"); scaling.EnemyPercentageList("Movement Speed", "Enemy movement speed increase per loop (additive).", Color.yellow, "+20%"); scaling.EnemyPercentageList("Projectile Speed", "Enemy projectile increase per loop (additive).", Color.cyan, "+20%"); scaling.NumberList("Room Wave Enemies", "Extra enemies added to room waves per loop.", "+2"); } private static void BuildEconomyMenu() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) Gunfig obj = gunfig.AddSubMenu("Economy Scaling"); obj.EconomyPercentageList("Shop Pricing", "Shop price and payout increase per loop (additive).", Color.green, "20%"); obj.AddLabel(GunfigHelpers.Gray("")); obj.AddLabel(GunfigHelpers.Gray("Affects all shops using coins as currency.")); obj.AddLabel(GunfigHelpers.Gray("Also boosts Sell Creep payout for balance")); obj.AddLabel(GunfigHelpers.Gray("")); obj.EconomyPercentageList("Special NPC Pricing", "Special NPC price and payout increase per loop (additive)", Color.magenta, "20%"); obj.AddLabel(GunfigHelpers.Gray("")); obj.AddLabel(GunfigHelpers.Gray("Special NPCs include Gunsling King,")); obj.AddLabel(GunfigHelpers.Gray("Synergrace, Vampire, and Winchester")); obj.AddLabel(GunfigHelpers.Gray("")); } private static void ApplyScalingPreset(Gunfig scaling, string preset) { ScalingPresetDefinition scalingPresetDefinition = ScalingPresetDefinitions.FirstOrDefault((ScalingPresetDefinition p) => p.Name == preset); if (scalingPresetDefinition == null) { return; } _applyingScalingPreset = true; try { scaling.SetPendingValue("Health", scalingPresetDefinition.Hp); scaling.SetPendingValue("Attack Speed", scalingPresetDefinition.AtkSpeed); scaling.SetPendingValue("Movement Speed", scalingPresetDefinition.MoveSpeed); scaling.SetPendingValue("Projectile Speed", scalingPresetDefinition.ProjSpeed); scaling.SetPendingValue("Room Wave Enemies", scalingPresetDefinition.Enemies); } finally { _applyingScalingPreset = false; } } private static string GetMatchingScalingPreset(Gunfig scaling) { string pendingValue = scaling.GetPendingValue("Health"); string pendingValue2 = scaling.GetPendingValue("Attack Speed"); string pendingValue3 = scaling.GetPendingValue("Movement Speed"); string pendingValue4 = scaling.GetPendingValue("Projectile Speed"); string pendingValue5 = scaling.GetPendingValue("Room Wave Enemies"); foreach (ScalingPresetDefinition scalingPresetDefinition in ScalingPresetDefinitions) { if (pendingValue == scalingPresetDefinition.Hp && pendingValue2 == scalingPresetDefinition.AtkSpeed && pendingValue3 == scalingPresetDefinition.MoveSpeed && pendingValue4 == scalingPresetDefinition.ProjSpeed && pendingValue5 == scalingPresetDefinition.Enemies) { return scalingPresetDefinition.Name; } } bool num = pendingValue == PercentageOptions[PercentageOptions.Count - 1] && pendingValue2 == PercentageOptions[PercentageOptions.Count - 1] && pendingValue3 == PercentageOptions[PercentageOptions.Count - 1] && pendingValue4 == PercentageOptions[PercentageOptions.Count - 1]; bool flag = pendingValue5 == NumberOptions[NumberOptions.Count - 1]; if (num && flag) { return "Game Breaking"; } return "Custom"; } private static void UpdateScalingPreset(Gunfig scaling) { if (!_applyingScalingPreset) { string matchingScalingPreset = GetMatchingScalingPreset(scaling); scaling.SetPendingValue("Scaling Preset", matchingScalingPreset); } } private static void ApplyGunfigScaling() { try { ProjectileSpeedScaler.ApplyFromCurrentScaling(); } catch { } } internal static float GetHpPerLoop() { return ParsePercentage(gunfig.Value("Health")); } internal static float GetAtkSpeedPerLoop() { return ParsePercentage(gunfig.Value("Attack Speed")); } internal static float GetMoveSpeedPerLoop() { return ParsePercentage(gunfig.Value("Movement Speed")); } internal static float GetProjSpeedPerLoop() { return ParsePercentage(gunfig.Value("Projectile Speed")); } internal static float GetShopPricingPerLoop() { return ParsePercentage(gunfig.Value("Shop Pricing")); } internal static float GetSpecialPricingPerLoop() { return ParsePercentage(gunfig.Value("Special NPC Pricing")); } internal static int GetExtraEnemiesPerLoop() { if (int.TryParse(gunfig.Value("Room Wave Enemies"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } return 0; } private static float ParsePercentage(string value) { if (string.IsNullOrEmpty(value)) { return 0f; } value = value.Replace("%", "").Trim(); if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return result / 100f; } private static void ToggleDPS(this Gunfig gunfig, string toggleName, string toggleDesc) { string item = GunfigHelpers.Red(toggleDesc); gunfig.AddScrollBox(toggleName, DefaultDisabled, (string)null, (Action<string, string>)delegate(string _, string value) { try { BossDpsCapPatch.ApplyBossDpsCapSettingToExistingBosses(value == "Yeah"); } catch { } }, new List<string> { item, item }, (Update)1); } private static void ToggleLOTJ(this Gunfig gunfig, string toggleName, string toggleDesc) { string item = GunfigHelpers.Magenta(toggleDesc); gunfig.AddScrollBox(toggleName, DefaultDisabled, (string)null, (Action<string, string>)delegate { try { LordOfTheJammedPatch.ApplyLive(); } catch { } }, new List<string> { item, item }, (Update)1); } private static void ToggleTAX(this Gunfig gunfig, string toggleName, string toggleDesc) { string item = GunfigHelpers.Green(toggleDesc); gunfig.AddScrollBox(toggleName, DefaultEnabled, (string)null, (Action<string, string>)null, new List<string> { item, item }, (Update)1); } private static void EnemyPercentageList(this Gunfig gunfig, string toggleName, string toggleDesc, Color color, string defaultValue) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) string element = GunfigHelpers.WithColor(toggleDesc, color); gunfig.AddScrollBox(toggleName, GunfigHelpers.WithDefault(PercentageOptions, defaultValue), (string)null, (Action<string, string>)delegate { ApplyGunfigScaling(); }, Enumerable.Repeat(element, PercentageOptions.Count).ToList(), (Update)1); } private static void EconomyPercentageList(this Gunfig gunfig, string toggleName, string toggleDesc, Color color, string defaultValue) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) string element = GunfigHelpers.WithColor(toggleDesc, color); gunfig.AddScrollBox(toggleName, GunfigHelpers.WithDefault(PercentageOptions, defaultValue), (string)null, (Action<string, string>)null, Enumerable.Repeat(element, PercentageOptions.Count).ToList(), (Update)1); } private static void NumberList(this Gunfig gunfig, string toggleName, string toggleDesc, string defaultValue) { string element = GunfigHelpers.Magenta(toggleDesc); gunfig.AddScrollBox(toggleName, GunfigHelpers.WithDefault(NumberOptions, defaultValue), (string)null, (Action<string, string>)delegate { ApplyGunfigScaling(); }, Enumerable.Repeat(element, NumberOptions.Count).ToList(), (Update)1); } } } namespace LoopTheGungeon.Config { public static class DataStorage { [Serializable] public class Data { public bool DebugMode; public int SavedLoopOffset; public bool ResumePending; public string ResumeRunGuid = string.Empty; public string ResumeOfferedMasteries = string.Empty; public string ResumeRunSummary = string.Empty; } private static readonly string DataPath = Path.Combine(ETGMod.ResourcesDirectory, "data.loopthegungeon.json"); private static readonly string TempPath = DataPath + ".tmp"; private static readonly object _ioLock = new object(); public static Data DataStore = new Data(); private static bool _debugMode; public static bool DebugMode { get { try { return _debugMode; } catch { return false; } } } public static void Init() { Load(); } public static void Load() { bool flag = false; try { lock (_ioLock) { if (!File.Exists(DataPath)) { DataStore = new Data(); flag = true; } else { string text = File.ReadAllText(DataPath); if (string.IsNullOrEmpty(text) || text.Trim().Length == 0) { DataStore = new Data(); flag = true; } else { DataStore = JsonUtility.FromJson<Data>(text) ?? new Data(); } } Normalize(); _debugMode = DataStore.DebugMode; } if (flag) { Save(); } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] Failed to load persistent data: {1}", "Loop The Gungeon", arg)); DataStore = new Data(); Normalize(); _debugMode = DataStore.DebugMode; } } public static void ReloadDebugMode() { try { if (!File.Exists(DataPath)) { return; } string text = File.ReadAllText(DataPath); if (!string.IsNullOrEmpty(text) && text.Trim().Length != 0) { Data data = JsonUtility.FromJson<Data>(text); if (data != null) { _debugMode = data.DebugMode; } } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] Failed to reload debug mode: {1}", "Loop The Gungeon", arg)); } } public static void Save() { try { lock (_ioLock) { if (DataStore == null) { DataStore = new Data(); } Normalize(); DataStore.DebugMode = _debugMode; string contents = JsonUtility.ToJson((object)DataStore, true); File.WriteAllText(TempPath, contents); ReplaceAtomic(TempPath, DataPath); } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] Failed to save persistent data: {1}", "Loop The Gungeon", arg)); try { if (File.Exists(TempPath)) { File.Delete(TempPath); } } catch { } } } public static void ClearResume() { try { if (DataStore == null) { DataStore = new Data(); } DataStore.ResumePending = false; DataStore.SavedLoopOffset = 0; DataStore.ResumeRunGuid = string.Empty; DataStore.ResumeOfferedMasteries = string.Empty; DataStore.ResumeRunSummary = string.Empty; Save(); } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] Failed to clear resume data: {1}", "Loop The Gungeon", arg)); } } private static void Normalize() { if (DataStore == null) { DataStore = new Data(); } if (DataStore.ResumeRunGuid == null) { DataStore.ResumeRunGuid = string.Empty; } if (DataStore.ResumeOfferedMasteries == null) { DataStore.ResumeOfferedMasteries = string.Empty; } if (DataStore.ResumeRunSummary == null) { DataStore.ResumeRunSummary = string.Empty; } } private static void ReplaceAtomic(string tempPath, string destinationPath) { if (File.Exists(destinationPath)) { try { File.Replace(tempPath, destinationPath, null); return; } catch { } } if (File.Exists(destinationPath)) { File.Delete(destinationPath); } File.Move(tempPath, destinationPath); } } } namespace LoopTheGungeon.Visuals { public static class ShrineSpritesOverride { private readonly struct CreatedKey : IEquatable<CreatedKey> { public readonly string CollectionStable; public readonly string Name; public CreatedKey(tk2dSpriteCollectionData c, string name) { CollectionStable = GetCollectionStableId(c); Name = name ?? string.Empty; } public bool Equals(CreatedKey other) { if (string.Equals(CollectionStable, other.CollectionStable, StringComparison.Ordinal)) { return string.Equals(Name, other.Name, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is CreatedKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (17 * 31 + StringComparer.Ordinal.GetHashCode(CollectionStable ?? "")) * 31 + StringComparer.Ordinal.GetHashCode(Name ?? ""); } } private readonly struct MatKey : IEquatable<MatKey> { public readonly int BaseMatId; public readonly int TexId; public MatKey(Material baseMat, Texture2D tex) { BaseMatId = (Object.op_Implicit((Object)(object)baseMat) ? ((Object)baseMat).GetInstanceID() : 0); TexId = (Object.op_Implicit((Object)(object)tex) ? ((Object)tex).GetInstanceID() : 0); } public bool Equals(MatKey other) { if (BaseMatId == other.BaseMatId) { return TexId == other.TexId; } return false; } public override bool Equals(object obj) { if (obj is MatKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (BaseMatId * 397) ^ TexId; } } private sealed class LruCache<TKey, TValue> { private struct Entry { public TKey Key; public TValue Value; } private readonly int _cap; private readonly Dictionary<TKey, LinkedListNode<Entry>> _map; private readonly LinkedList<Entry> _lru; private readonly Action<TValue> _onEvict; public int Count => _map.Count; public LruCache(int capacity, Action<TValue> onEvict) { _cap = Mathf.Max(1, capacity); _map = new Dictionary<TKey, LinkedListNode<Entry>>(_cap); _lru = new LinkedList<Entry>(); _onEvict = onEvict; } public bool TryGetValue(TKey key, out TValue value) { if (_map.TryGetValue(key, out var value2)) { _lru.Remove(value2); _lru.AddFirst(value2); value = value2.Value.Value; return true; } value = default(TValue); return false; } public void Set(TKey key, TValue value) { if (_map.TryGetValue(key, out var value2)) { value2.Value = new Entry { Key = key, Value = value }; _lru.Remove(value2); _lru.AddFirst(value2); return; } while (_map.Count >= _cap) { EvictOne(); } LinkedListNode<Entry> linkedListNode = new LinkedListNode<Entry>(new Entry { Key = key, Value = value }); _lru.AddFirst(linkedListNode); _map[key] = linkedListNode; } private void EvictOne() { LinkedListNode<Entry> last = _lru.Last; if (last == null) { return; } _lru.RemoveLast(); _map.Remove(last.Value.Key); if (_onEvict == null) { return; } try { _onEvict(last.Value.Value); } catch { } } public void Clear(bool destroyValues) { if (destroyValues && _onEvict != null) { foreach (Entry item in _lru) { try { _onEvict(item.Value); } catch { } } } _map.Clear(); _lru.Clear(); } } private const int MaxCachedMaterials = 64; private const int MaxCreatedIdMap = 128; private const int LEAD_LEGEND_TIER = 25; private const string INTERNAL_STATUE_PRE = "LOOP_STATUE_PRE"; private const string INTERNAL_STATUE_SPENT = "LOOP_STATUE_SPENT"; private const string INTERNAL_STATUE_LEGEND_PRE = "LOOP_STATUE_LEGEND_PRE"; private const string INTERNAL_STATUE_LEGEND_SPENT = "LOOP_STATUE_LEGEND_SPENT"; private const string INTERNAL_PEDESTAL_NORMAL = "LOOP_PEDESTAL_NORMAL"; private const string INTERNAL_PEDESTAL_LEGEND = "LOOP_PEDESTAL_LEGEND"; private static readonly LruCache<MatKey, Material> _matCache = new LruCache<MatKey, Material>(64, DestroyMaterial); private static readonly LruCache<CreatedKey, int> _createdIds = new LruCache<CreatedKey, int>(128, null); private static bool _tabletIconsInstalled; private static bool _portraitAtlasIconsInstalled; public static void ClearIds() { _createdIds.Clear(destroyValues: false); } public static void ClearRuntimeCaches(bool destroyUnityObjects) { _createdIds.Clear(destroyValues: false); _matCache.Clear(destroyUnityObjects); } public static void TryResetMasteryShrineVisuals() { try { bool spent = !MasteryShrineInteractable.AnyAlivePlayerCanUseShrine(); MasteryShrineInteractable masteryShrineInteractable = Object.FindObjectOfType<MasteryShrineInteractable>(); if ((Object)(object)masteryShrineInteractable != (Object)null) { Apply(((Component)masteryShrineInteractable).gameObject, spent); } } catch { } } private static bool IsLeadLegendUnlocked() { try { return RunSummary.HasMasteryTitleReached(25); } catch { return false; } } private static string GetCollectionStableId(tk2dSpriteCollectionData c) { if (!Object.op_Implicit((Object)(object)c)) { return "null"; } if (!string.IsNullOrEmpty(c.spriteCollectionName)) { return c.spriteCollectionName; } return ((Object)c).name ?? "null"; } private static void DestroySafe(Object o) { try { if (o != (Object)null) { Object.Destroy(o); } } catch { } } private static void DestroyMaterial(Material m) { DestroySafe((Object)(object)m); } private static Material GetOrCreateMaterial(Material baseMat, Texture2D tex) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)baseMat) || !Object.op_Implicit((Object)(object)tex)) { return null; } MatKey key = new MatKey(baseMat, tex); if (_matCache.TryGetValue(key, out var value) && Object.op_Implicit((Object)(object)value)) { return value; } Material val = new Material(baseMat); val.mainTexture = (Texture)(object)tex; ((Object)val).hideFlags = (HideFlags)20; _matCache.Set(key, val); return val; } public static bool Apply(GameObject shrineRoot, bool spent) { try { if (!Object.op_Implicit((Object)(object)shrineRoot)) { return false; } bool flag = false; bool flag2 = IsLeadLegendUnlocked(); tk2dBaseSprite val = FindSpriteOnChild(shrineRoot.transform, "ShrineStatue"); if (Object.op_Implicit((Object)(object)val)) { Texture2D val2 = null; string text = null; if (flag2) { if (spent) { val2 = ModTextures.MasteryShrine.StatueLegendSpentTex; text = "LOOP_STATUE_LEGEND_SPENT"; } else { val2 = ModTextures.MasteryShrine.StatueLegendPreTex; text = "LOOP_STATUE_LEGEND_PRE"; } } else if (spent) { val2 = ModTextures.MasteryShrine.StatueSpentTex; text = "LOOP_STATUE_SPENT"; } else { val2 = ModTextures.MasteryShrine.StatuePreTex; text = "LOOP_STATUE_PRE"; } flag |= SwapSpriteSingle(val, val2, text); } tk2dBaseSprite val3 = FindSpriteOnChild(shrineRoot.transform, "shrine_Base"); if (Object.op_Implicit((Object)(object)val3)) { Texture2D tex = (flag2 ? ModTextures.MasteryShrine.PedestalLegendTex : ModTextures.MasteryShrine.PedestalNormalTex); string internalName = (flag2 ? "LOOP_PEDESTAL_LEGEND" : "LOOP_PEDESTAL_NORMAL"); flag |= SwapSpriteSingle(val3, tex, internalName); } return flag; } catch { return false; } } private static tk2dBaseSprite FindSpriteOnChild(Transform root, string childName) { try { if (!Object.op_Implicit((Object)(object)root)) { return null; } Transform val = root.Find(childName); if (!Object.op_Implicit((Object)(object)val)) { return null; } return ((Component)val).GetComponent<tk2dBaseSprite>(); } catch { return null; } } private static bool SwapSpriteSingle(tk2dBaseSprite spr, Texture2D tex, string internalName) { try { if (!Object.op_Implicit((Object)(object)spr) || !Object.op_Implicit((Object)(object)tex)) { return false; } tk2dSpriteCollectionData collection = spr.Collection; if (!Object.op_Implicit((Object)(object)collection) || collection.spriteDefinitions == null) { return false; } int num = spr.spriteId; if (num < 0 || num >= collection.spriteDefinitions.Length) { num = 0; } int num2 = AddTexAsSpriteDef(collection, num, tex, internalName); if (num2 < 0) { return false; } spr.SetSprite(collection, num2); spr.ForceBuild(); ModTextures.Tk2dUpdateMaterialSafe(spr); return true; } catch { return false; } } public static int AddTexAsSpriteDef(tk2dSpriteCollectionData collection, int templateSpriteId, Texture2D tex, string name, float worldHeight = -1f) { CreatedKey key = new CreatedKey(collection, name); if (_createdIds.TryGetValue(key, out var value) && value >= 0) { return value; } try { if (!Object.op_Implicit((Object)(object)collection) || !Object.op_Implicit((Object)(object)tex)) { return -1; } tk2dSpriteDefinition[] array = collection.spriteDefinitions; if (array == null || array.Length == 0) { return -1; } try { tk2dSpriteDefinition[] spriteDefinitions = collection.spriteDefinitions; if (spriteDefinitions != null) { for (int i = 0; i < spriteDefinitions.Length; i++) { tk2dSpriteDefinition val = spriteDefinitions[i]; if (val != null && string.Equals(val.name, name, StringComparison.Ordinal)) { _createdIds.Set(new CreatedKey(collection, name), i); return i; } } } } catch { } tk2dSpriteDefinition val2 = array[Mathf.Clamp(templateSpriteId, 0, array.Length - 1)]; if (val2 == null) { return -1; } tk2dSpriteDefinition val3 = CloneDefinition(val2, tex, name); ResizeDefByPixelRatio(val3, val2, tex); if (worldHeight > 0f) { ResizeDefinitionToWorldHeight(val3, tex, worldHeight); } int num = array.Length; Array.Resize(ref array, num + 1); array[num] = val3; collection.spriteDefinitions = array; _createdIds.Set(key, num); return num; } catch { return -1; } } public static int AddBadgeSpriteDef(tk2dSpriteCollectionData collection, Material baseMat, Texture2D tex, string name, float pixelsPerUnit = 16f) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: 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_018c: 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_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)collection) || !Object.op_Implicit((Object)(object)tex) || !Object.op_Implicit((Object)(object)baseMat)) { return -1; } int num = Mathf.RoundToInt(pixelsPerUnit * 1000f); string text = $"{name}__ppu{num}"; CreatedKey key = new CreatedKey(collection, text); if (_createdIds.TryGetValue(key, out var value) && value >= 0) { return value; } tk2dSpriteDefinition[] spriteDefinitions = collection.spriteDefinitions; if (spriteDefinitions != null) { for (int i = 0; i < spriteDefinitions.Length; i++) { tk2dSpriteDefinition val = spriteDefinitions[i]; if (val != null && string.Equals(val.name, text, StringComparison.Ordinal)) { _createdIds.Set(key, i); return i; } } } try { tk2dSpriteDefinition val2 = new tk2dSpriteDefinition(); val2.name = text; val2.materialInst = GetOrCreateMaterial(baseMat, tex); val2.uvs = (Vector2[])(object)new Vector2[4]; val2.uvs[0] = new Vector2(0f, 0f); val2.uvs[1] = new Vector2(1f, 0f); val2.uvs[2] = new Vector2(0f, 1f); val2.uvs[3] = new Vector2(1f, 1f); float num2 = ((pixelsPerUnit <= 0.0001f) ? 0.0625f : (1f / pixelsPerUnit)); float num3 = (float)((Texture)tex).width * num2; float num4 = (float)((Texture)tex).height * num2; float num5 = num3 * 0.5f; float num6 = num4 * 0.5f; val2.position0 = new Vector3(0f - num5, 0f - num6, 0f); val2.position1 = new Vector3(num5, 0f - num6, 0f); val2.position2 = new Vector3(0f - num5, num6, 0f); val2.position3 = new Vector3(num5, num6, 0f); val2.texelSize = new Vector2(num2, num2); int num7 = collection.spriteDefinitions.Length; Array.Resize(ref collection.spriteDefinitions, num7 + 1); collection.spriteDefinitions[num7] = val2; _createdIds.Set(key, num7); return num7; } catch { return -1; } } private static tk2dSpriteDefinition CloneDefinition(tk2dSpriteDefinition template, Texture2D newTex, string name) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown tk2dSpriteDefinition val = new tk2dSpriteDefinition(); CopyAllInstanceFields(template, val); val.name = name; if ((Object)(object)val.materialInst != (Object)null) { val.materialInst = GetOrCreateMaterial(val.materialInst, newTex); } SetFullUvsIfPresent(val); return val; } private static void CopyAllInstanceFields(object src, object dst) { if (src == null || dst == null) { return; } Type type = src.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { try { if (!fieldInfo.IsInitOnly) { object obj = fieldInfo.GetValue(src); if (obj != null && obj.GetType().IsArray) { obj = ((Array)obj).Clone(); } fieldInfo.SetValue(dst, obj); } } catch { } } } private static void SetFullUvsIfPresent(tk2dSpriteDefinition def) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_007e: Unknown result type (might be due to invalid IL or missing references) if (def != null && def.uvs != null && def.uvs.Length >= 4) { def.uvs[0] = new Vector2(0f, 0f); def.uvs[1] = new Vector2(1f, 0f); def.uvs[2] = new Vector2(0f, 1f); def.uvs[3] = new Vector2(1f, 1f); } } private static void ResizeDefinitionToWorldHeight(tk2dSpriteDefinition def, Texture2D tex, float worldHeight) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (def != null && !((Object)(object)tex == (Object)null) && ((Texture)tex).width > 0 && ((Texture)tex).height > 0) { float num = (float)((Texture)tex).width / (float)((Texture)tex).height; float num2 = worldHeight * num * 0.5f; float num3 = worldHeight * 0.5f; Vector3 position = default(Vector3); ((Vector3)(ref position))..ctor(0f - num2, 0f - num3, 0f); Vector3 position2 = default(Vector3); ((Vector3)(ref position2))..ctor(num2, 0f - num3, 0f); Vector3 position3 = default(Vector3); ((Vector3)(ref position3))..ctor(0f - num2, num3, 0f); Vector3 position4 = default(Vector3); ((Vector3)(ref position4))..ctor(num2, num3, 0f); def.position0 = position; def.position1 = position2; def.position2 = position3; def.position3 = position4; } } private static void ResizeDefByPixelRatio(tk2dSpriteDefinition def, tk2dSpriteDefinition template, Texture2D newTex) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if (def == null || template == null || (Object)(object)newTex == (Object)null || ((Texture)newTex).width <= 0 || ((Texture)newTex).height <= 0) { return; } Vector3 position = template.position0; Vector3 position2 = template.position1; Vector3 position3 = template.position2; _ = template.position3; Vector3 val = position2 - position; Vector3 val2 = position3 - position; float magnitude = ((Vector3)(ref val)).magnitude; float magnitude2 = ((Vector3)(ref val2)).magnitude; if (magnitude <= 0.0001f || magnitude2 <= 0.0001f) { return; } Vector2 texelSize = template.texelSize; if (!(texelSize.x <= 1E-06f) && !(texelSize.y <= 1E-06f)) { float num = magnitude / texelSize.x; float num2 = magnitude2 / texelSize.y; if (!(num <= 1f) && !(num2 <= 1f)) { float num3 = (float)((Texture)newTex).width / num; float num4 = (float)((Texture)newTex).height / num2; Vector3 val3 = val * num3; Vector3 val4 = val2 * num4; Vector3 val5 = (position + position2) * 0.5f; def.position0 = val5 - 0.5f * val3; def.position1 = val5 + 0.5f * val3; def.position2 = def.position0 + val4; def.position3 = def.position1 + val4; } } } internal static void TryInstallUiIcons() { TryInstallTabletIcons(); TryInstallConversationAtlasIcons(); } internal static void TryInstallTabletIcons() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown if (_tabletIconsInstalled) { return; } try { GameObject val = (GameObject)ResourceCache.Acquire("ControllerButtonSprite"); tk2dBaseSprite val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<tk2dBaseSprite>() : null); tk2dSpriteCollectionData val3 = (Object.op_Implicit((Object)(object)val2) ? val2.Collection : null); if (!((Object)(object)val3 == (Object)null)) { EnsureSpriteInCollection(val3, ModTextures.UI.HellRoundTex, "loop_hell_round"); EnsureSpriteInCollection(val3, ModTextures.UI.CoolnessTex, "loop_coolness"); EnsureSpriteInCollection(val3, ModTextures.UI.CurseTex, "loop_curse"); EnsureSpriteInCollection(val3, ModTextures.UI.GlassTex, "loop_glass"); EnsureSpriteInCollection(val3, ModTextures.UI.RageTex, "loop_rage"); _tabletIconsInstalled = true; } } catch { } } internal static void TryInstallConversationAtlasIcons() { if (_portraitAtlasIconsInstalled) { return; } try { GameUIRoot instance = GameUIRoot.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.ConversationBar == (Object)null)) { dfSprite portraitSprite = instance.ConversationBar.portraitSprite; if (!((Object)(object)portraitSprite == (Object)null) && !((Object)(object)portraitSprite.Atlas == (Object)null)) { EnsureSpriteInAtlas(portraitSprite.Atlas, ModTextures.UI.HellRoundBoxTex, "loop_hell_round_box"); _portraitAtlasIconsInstalled = true; } } } catch { } } private static void EnsureSpriteInCollection(tk2dSpriteCollectionData col, Texture2D tex, string name) { if ((Object)(object)col == (Object)null || (Object)(object)tex == (Object)null || string.IsNullOrEmpty(name)) { return; } tk2dSpriteDefinition[] spriteDefinitions = col.spriteDefinitions; if (spriteDefinitions != null) { for (int i = 0; i < spriteDefinitions.Length; i++) { if (spriteDefinitions[i] != null && spriteDefinitions[i].name == name) { return; } } } SpriteBuilder.AddSpriteToCollection(tex, col, name); } private static void EnsureSpriteInAtlas(dfAtlas atlas, Texture2D tex, string name) { if ((Object)(object)atlas == (Object)null || (Object)(object)tex == (Object)null || string.IsNullOrEmpty(name)) { return; } try { if (atlas[name] != (ItemInfo)null) { return; } } catch { } try { ToolsCharApi.AddNewItemToAtlas(atlas, tex, name); } catch { } } } internal static class ModTextures { internal static class MasteryShrine { public static Texture2D StatuePreTex => LoadPng("Resources/MasteryShrine", "statue_pre.png", (FilterMode)0); public static Texture2D StatueSpentTex => LoadPng("Resources/MasteryShrine", "statue_spent.png", (FilterMode)0); public static Texture2D StatueLegendPreTex => LoadPng("Resources/MasteryShrine", "statue_legend_pre.png", (FilterMode)0); public static Texture2D StatueLegendSpentTex => LoadPng("Resources/MasteryShrine", "statue_legend_spent.png", (FilterMode)0); public static Texture2D PedestalNormalTex => LoadPng("Resources/MasteryShrine", "pedestal_normal.png", (FilterMode)0); public static Texture2D PedestalLegendTex => LoadPng("Resources/MasteryShrine", "pedestal_legend.png", (FilterMode)0); public static Texture2D Mr1LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_1.png", (FilterMode)0); public static Texture2D Mr2LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_2.png", (FilterMode)0); public static Texture2D Mr3LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_3.png", (FilterMode)0); public static Texture2D Mr4LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_4.png", (FilterMode)0); public static Texture2D Mr5LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_5.png", (FilterMode)0); public static Texture2D Mr6LitTex => LoadPng("Resources/MasteryShrine", "mastery_badge_6.png", (FilterMode)0); public static Texture2D Mr6LitLegendTex => LoadPng("Resources/MasteryShrine", "mastery_badge_6_legend.png", (FilterMode)0); } internal static class LoopButton { public static Texture2D Talk1Tex => LoadPng("Resources/LoopButton", "loop_button_talk_1.png", (FilterMode)0); public static Texture2D Talk2Tex => LoadPng("Resources/LoopButton", "loop_button_talk_2.png", (FilterMode)0); public static Texture2D Talk3Tex => LoadPng("Resources/LoopButton", "loop_button_talk_3.png", (FilterMode)0); } internal static class TitleScreen { public static Texture2D Looper1Tex => LoadPng("Resources/TitleScreen", "looper_1.png", (FilterMode)0); public static Texture2D Looper2Tex => LoadPng("Resources/TitleScreen", "looper_2.png", (FilterMode)0); public static Texture2D Looper3Tex => LoadPng("Resources/TitleScreen", "looper_3.png", (FilterMode)0); public static Texture2D Looper4Tex => LoadPng("Resources/TitleScreen", "looper_4.png", (FilterMode)0); public static Texture2D Looper5Tex => LoadPng("Resources/TitleScreen", "looper_5.png", (FilterMode)0); public static Texture2D Looper6Tex => LoadPng("Resources/TitleScreen", "looper_6.png", (FilterMode)0); public static Texture2D Looper7Tex => LoadPng("Resources/TitleScreen", "looper_7.png", (FilterMode)0); public static Texture2D Looper8Tex => LoadPng("Resources/TitleScreen", "looper_8.png", (FilterMode)0); } internal static class UI { public static Texture2D CoolnessTex => LoadPng("Resources/UI", "coolness_icon.png", (FilterMode)0); public static Texture2D GlassTex => LoadPng("Resources/UI", "glass_icon.png", (FilterMode)0); public static Texture2D CurseTex => LoadPng("Resources/UI", "curse_icon.png", (FilterMode)0); public static Texture2D RageTex => LoadPng("Resources/UI", "rage_icon.png", (FilterMode)0); public static Texture2D HellRoundTex => LoadPng("Resources/UI", "hell_round_icon.png", (FilterMode)0); public static Texture2D HellRoundBoxTex => LoadPng("Resources/UI", "hell_round_box_icon.png", (FilterMode)0); } private sealed class CacheEntry { public Texture2D Tex; public long LooseLastWriteUtcTicks; } public static bool PreferLooseFiles = false; private static readonly string PluginDir = ResolvePluginDir(); private static readonly Dictionary<string, CacheEntry> _cache = new Dictionary<string, CacheEntry>(StringComparer.OrdinalIgnoreCase); private static Action<tk2dBaseSprite> _tk2dUpdateMaterial; private static string ResolvePluginDir() { try { string text = null; try { Plugin instance = Plugin.Instance; object obj; if (instance == null) { obj = null; } else { PluginInfo info = ((BaseUnityPlugin)instance).Info; obj = ((info != null) ? info.Location : null); } text = (string)obj; } catch { } if (string.IsNullOrEmpty(text)) { text = Assembly.GetExecutingAssembly().Location; } return string.IsNullOrEmpty(text) ? string.Empty : Path.GetDirectoryName(text); } catch { return string.Empty; } } private static Texture2D LoadPng(string folderUnderPluginDir, string fileName, FilterMode filter) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(fileName)) { return null; } string cacheKey = folderUnderPluginDir + "/" + fileName + "|f=" + (int)filter; if (PreferLooseFiles && !string.IsNullOrEmpty(PluginDir)) { string fullPath = Path.Combine(Path.Combine(PluginDir, folderUnderPluginDir.Replace('/', Path.DirectorySeparatorChar)), fileName); Texture2D val = TryLoadLoosePngCached(cacheKey, fullPath, filter); if ((Object)(object)val != (Object)null) { return val; } } return TryLoadEmbeddedPngCached(cacheKey, folderUnderPluginDir, fileName, filter); } private static Texture2D TryLoadLoosePngCached(string cacheKey, string fullPath, FilterMode filter) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) { return null; } long ticks = File.GetLastWriteTimeUtc(fullPath).Ticks; if (_cache.TryGetValue(cacheKey, out var value) && value != null && (Object)(object)value.Tex != (Object)null && value.LooseLastWriteUtcTicks == ticks) { return value.Tex; } byte[] array = File.ReadAllBytes(fullPath); Texture2D val = new Texture2D(2, 2, (TextureFormat)5, false); if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); return null; } ((Texture)val).filterMode = filter; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Object)val).hideFlags = (HideFlags)20; if (value != null && (Object)(object)value.Tex != (Object)null) { Object.Destroy((Object)(object)value.Tex); } _cache[cacheKey] = new CacheEntry { Tex = val, LooseLastWriteUtcTicks = ticks }; return val; } catch { return null; } } private static Texture2D TryLoadEmbeddedPngCached(string cacheKey, string folderUnderRoot, string fileName, FilterMode filter) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) try { if (_cache.TryGetValue(cacheKey, out var value) && value != null && (Object)(object)value.Tex != (Object)null && value.LooseLastWriteUtcTicks == 0L) { return value.Tex; } string text = folderUnderRoot.Replace('/', '.').Replace('\\', '.'); string name = "LoopTheGungeon." + text + "." + fileName; Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(name); if (stream == null) { string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); for (int i = 0; i < manifestResourceNames.Length; i++) { ETGModConsole.Log((object)manifestResourceNames[i], false); } return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)5, false); if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); return null; } ((Texture)val).filterMode = filter; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Object)val).hideFlags = (HideFlags)20; if (value != null && (Object)(object)value.Tex != (Object)null) { Object.Destroy((Object)(object)value.Tex); } _cache[cacheKey] = new CacheEntry { Tex = val, LooseLastWriteUtcTicks = 0L }; return val; } catch { return null; } } public static void ClearAllCachedTextures(bool destroyUnityObjects) { if (destroyUnityObjects) { foreach (KeyValuePair<string, CacheEntry> item in _cache) { try { if ((Object)(object)item.Value?.Tex != (Object)null) { Object.Destroy((Object)(object)item.Value.Tex); } } catch { } } } _cache.Clear(); } public static void Tk2dUpdateMaterialSafe(tk2dBaseSprite spr) { if ((Object)(object)spr == (Object)null) { return; } try { if (_tk2dUpdateMaterial == null) { MethodInfo method = typeof(tk2dBaseSprite).GetMethod("UpdateMaterial", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)method == null) { return; } _tk2dUpdateMaterial = (Action<tk2dBaseSprite>)Delegate.CreateDelegate(typeof(Action<tk2dBaseSprite>), null, method); } _tk2dUpdateMaterial(spr); } catch { } } } public static class ButtonSpritesOverride { private sealed class InstancedTk2dMarker : MonoBehaviour { } public class ButtonSpriteSetup : MonoBehaviour { public bool Applied; } private sealed class RuntimeAtlas { public Texture2D AtlasTex; public Material Mat; } private readonly struct CreatedKey : IEquatable<CreatedKey> { public readonly string CollectionStable; public readonly string Name; public CreatedKey(tk2dSpriteCollectionData c, string name) { CollectionStable = GetCollectionStableId(c); Name = name ?? string.Empty; } public bool Equals(CreatedKey other) { if (string.Equals(CollectionStable, other.CollectionStable, StringComparison.Ordinal)) { return string.Equals(Name, other.Name, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is CreatedKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (17 * 31 + StringComparer.Ordinal.GetHashCode(CollectionStable ?? "")) * 31 + StringComparer.Ordinal.GetHashCode(Name ?? ""); } } private readonly struct AtlasKey : IEquatable<AtlasKey> { public readonly string ShaderName; public readonly string BaseAtlasName; public readonly int BaseW; public readonly int BaseH; public AtlasKey(Material templateMat) { Texture2D val = (Texture2D)(((Object)(object)templateMat != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); ShaderName = ((Object.op_Implicit((Object)(object)templateMat) && Object.op_Implicit((Object)(object)templateMat.shader)) ? (((Object)templateMat.shader).name ?? "") : ""); BaseAtlasName = (Object.op_Implicit((Object)(object)val) ? (((Object)val).name ?? "") : ""); BaseW = (Object.op_Implicit((Object)(object)val) ? ((Texture)val).width : 0); BaseH = (Object.op_Implicit((Object)(object)val) ? ((Texture)val).height : 0); } public bool Equals(AtlasKey other) { if (BaseW == other.BaseW && BaseH == other.BaseH && string.Equals(ShaderName, other.ShaderName, StringComparison.Ordinal)) { return string.Equals(BaseAtlasName, other.BaseAtlasName, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is AtlasKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((17 * 31 + BaseW) * 31 + BaseH) * 31 + StringComparer.Ordinal.GetHashCode(ShaderName ?? "")) * 31 + StringComparer.Ordinal.GetHashCode(BaseAtlasName ?? ""); } } private readonly struct MatKey : IEquatable<MatKey> { public readonly string ShaderName; public readonly string BaseMatName; public readonly string TexName; public readonly int TexW; public readonly int TexH; public MatKey(Material baseMat, Texture2D tex) { ShaderName = ((Object.op_Implicit((Object)(object)baseMat) && Object.op_Implicit((Object)(object)baseMat.shader)) ? (((Object)baseMat.shader).name ?? "") : ""); BaseMatName = (Object.op_Implicit((Object)(object)baseMat) ? (((Object)baseMat).name ?? "") : ""); TexName = (Object.op_Implicit((Object)(object)tex) ? (((Object)tex).name ?? "") : ""); TexW = (Object.op_Implicit((Object)(object)tex) ? ((Texture)tex).width : 0); TexH = (Object.op_Implicit((Object)(object)tex) ? ((Texture)tex).height : 0); } public bool Equals(MatKey other) { if (TexW == other.TexW && TexH == other.TexH && string.Equals(ShaderName, other.ShaderName, StringComparison.Ordinal) && string.Equals(BaseMatName, other.BaseMatName, StringComparison.Ordinal)) { return string.Equals(TexName, other.TexName, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is MatKey other) { return Equals(other); } return false; } public override int GetHashCode() { return ((((17 * 31 + TexW) * 31 + TexH) * 31 + StringComparer.Ordinal.GetHashCode(ShaderName ?? "")) * 31 + StringComparer.Ordinal.GetHashCode(BaseMatName ?? "")) * 31 + StringComparer.Ordinal.GetHashCode(TexName ?? ""); } } private sealed class LruCache<TKey, TValue> { private struct Entry { public TKey Key; public TValue Value; } private readonly int _cap; private readonly Dictionary<TKey, LinkedListNode<Entry>> _map; private readonly LinkedList<Entry> _lru; private readonly Action<TValue> _onEvict; public int Count => _map.Count; public LruCache(int capacity, Action<TValue> onEvict) { _cap = Mathf.Max(1, capacity); _map = new Dictionary<TKey, LinkedListNode<Entry>>(_cap); _lru = new LinkedList<Entry>(); _onEvict = onEvict; } public bool TryGetValue(TKey key, out TValue value) { if (_map.TryGetValue(key, out var value2)) { _lru.Remove(value2); _lru.AddFirst(value2); value = value2.Value.Value; return true; } value = default(TValue); return false; } public void Set(TKey key, TValue value) { if (_map.TryGetValue(key, out var value2)) { value2.Value = new Entry { Key = key, Value = value }; _lru.Remove(value2); _lru.AddFirst(value2); return; } while (_map.Count >= _cap) { EvictOne(); } LinkedListNode<Entry> linkedListNode = new LinkedListNode<Entry>(new Entry { Key = key, Value = value }); _lru.AddFirst(linkedListNode); _map[key] = linkedListNode; } private void EvictOne() { LinkedListNode<Entry> last = _lru.Last; if (last == null) { return; } _lru.RemoveLast(); _map.Remove(last.Value.Key); if (_onEvict == null) { return; } try { _onEvict(last.Value.Value); } catch { } } public void Clear(bool destroyValues) { if (destroyValues && _onEvict != null) { foreach (Entry item in _lru) { try { _onEvict(item.Value); } catch { } } } _map.Clear(); _lru.Clear(); } } private readonly struct Key : IEquatable<Key> { public readonly tk2dSpriteCollectionData Col; public readonly int SpriteId; public Key(tk2dSpriteCollectionData col, int id) { Col = col; SpriteId = id; } public bool Equals(Key other) { if (Col == other.Col) { return SpriteId == other.SpriteId; } return false; } public override bool Equals(object obj) { if (obj is Key other) { return Equals(other); } return false; } public override int GetHashCode() { return (17 * 31 + (Object.op_Implicit((Object)(object)Col) ? ((Object)Col).GetInstanceID() : 0)) * 31 + SpriteId; } } private const int MaxCachedMaterials = 128; private const int MaxCachedAtlases = 16; private const int MaxCreatedIdMap = 512; private static readonly LruCache<MatKey, Material> _matCache = new LruCache<MatKey, Material>(128, DestroyMaterial); private static readonly LruCache<AtlasKey, RuntimeAtlas> _atlasCache = new LruCache<AtlasKey, RuntimeAtlas>(16, DestroyRuntimeAtlas); private static readonly LruCache<CreatedKey, int> _createdIds = new LruCache<CreatedKey, int>(512, null); private static string GetCollectionStableId(tk2dSpriteCollectionData c) { if (!Object.op_Implicit((Object)(object)c)) { return "null"; } try { PropertyInfo property = ((object)c).GetType().GetProperty("spriteCollectionName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property != null) { string text = property.GetValue(c, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } return ((Object)c).name ?? "null"; } private static void DestroyMaterial(Material m) { try { if (Object.op_Implicit((Object)(object)m)) { Object.Destroy((Object)(object)m); } } catch { } } private static void DestroyRuntimeAtlas(RuntimeAtlas ra) { if (ra == null) { return; } try { if (Object.op_Implicit((Object)(object)ra.AtlasTex)) { Object.Destroy((Object)(object)ra.AtlasTex); } } catch { } try { if (Object.op_Implicit((Object)(object)ra.Mat)) { Object.Destroy((Object)(object)ra.Mat); } } catch { } } public static void ClearRuntimeCaches(bool destroyUnityObjects) { _createdIds.Clear(destroyValues: false); _matCache.Clear(destroyUnityObjects); _atlasCache.Clear(destroyUnityObjects); } private static Material GetOrCreateMaterial(Material baseMat, Texture2D tex) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)baseMat) || !Object.op_Implicit((Object)(object)tex)) { return null; } MatKey key = new MatKey(baseMat, tex); if (_matCache.TryGetValue(key, out var val