using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.Items;
using PerfectRandom.Sulfur.Core.UI;
using PerfectRandom.Sulfur.Core.UI.Inventory;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("RandomWeaponPerLevel")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RandomWeaponPerLevel")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("2c8445fc-70f2-4406-be3e-d54a3e8b4737")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RandomWeaponPerLevel;
public enum PreferredWeaponSlotMode
{
Weapon0,
Weapon1,
Random,
FirstAvailable
}
public sealed class RandomWeaponMarker : MonoBehaviour
{
public int generationId;
public int levelSignature;
public string reason;
public string weaponName;
}
[BepInPlugin("kumo.sulfur.random_weapon_per_level", "Random Weapon Per Level", "0.4.1")]
public sealed class Plugin : BaseUnityPlugin
{
private sealed class WeightedAttachmentCandidate
{
public ItemDefinition Item;
public float Weight;
public WeightedAttachmentCandidate(ItemDefinition item, float weight)
{
Item = item;
Weight = weight;
}
}
[CompilerGenerated]
private sealed class <AutoGiveForLevelSignature>d__78 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public int signature;
public string reason;
public Plugin <>4__this;
private float <delay>5__1;
private int <currentSignature>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <AutoGiveForLevelSignature>d__78(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>4__this.autoGiveRoutineRunning = true;
<delay>5__1 = Mathf.Max(0f, <>4__this.autoGiveDelayAfterLevelStart.Value);
if (<delay>5__1 > 0f)
{
<>2__current = (object)new WaitForSeconds(<delay>5__1);
<>1__state = 1;
return true;
}
goto IL_008c;
case 1:
<>1__state = -1;
goto IL_008c;
case 2:
{
<>1__state = -1;
<>4__this.autoGiveRoutineRunning = false;
return false;
}
IL_008c:
if (!<>4__this.enableMod.Value || !<>4__this.randomizeOnLevelStart.Value)
{
<>4__this.autoGivenLevelSignatures.Remove(signature);
<>4__this.autoGiveRoutineRunning = false;
return false;
}
if (!<>4__this.IsInPlayableLevel())
{
<>4__this.autoGivenLevelSignatures.Remove(signature);
<>4__this.sawLoadingOrLevelTransition = true;
<>4__this.autoGiveRoutineRunning = false;
return false;
}
<currentSignature>5__2 = <>4__this.GetCachedLevelSignature();
if (<currentSignature>5__2 != signature)
{
<>4__this.autoGivenLevelSignatures.Remove(signature);
<>4__this.sawLoadingOrLevelTransition = true;
<>4__this.autoGiveRoutineRunning = false;
return false;
}
<>2__current = <>4__this.GiveRandomWeaponRoutine(reason + " " + signature, signature);
<>1__state = 2;
return true;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <GiveRandomWeaponRoutine>d__82 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public string reason;
public int levelSignature;
public Plugin <>4__this;
private ItemGrid <backpack>5__1;
private WeaponSO <weapon>5__2;
private InventorySlot <targetSlot>5__3;
private HashSet<InventoryItem> <beforeItems>5__4;
private int <waitFrames>5__5;
private InventoryItem <createdItem>5__6;
private int <removed>5__7;
private Exception <ex>5__8;
private int <i>5__9;
private int <appliedOilCount>5__10;
private int <appliedScrollCount>5__11;
private int <appliedAttachmentCount>5__12;
private string <itemInfo>5__13;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <GiveRandomWeaponRoutine>d__82(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<backpack>5__1 = null;
<weapon>5__2 = null;
<beforeItems>5__4 = null;
<createdItem>5__6 = null;
<ex>5__8 = null;
<itemInfo>5__13 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0191: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_014b: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Invalid comparison between Unknown and I4
//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0365: Unknown result type (might be due to invalid IL or missing references)
//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
//IL_04af: Unknown result type (might be due to invalid IL or missing references)
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<backpack>5__1 = <>4__this.GetPlayerBackpackGrid();
if ((Object)(object)<backpack>5__1 == (Object)null)
{
((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)"Could not give random weapon: PlayerBackpackGrid is null.");
return false;
}
<weapon>5__2 = <>4__this.PickRandomWeapon();
if ((Object)(object)<weapon>5__2 == (Object)null)
{
((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)"Could not give random weapon: no valid WeaponSO found.");
return false;
}
if (<>4__this.cleanupOldGeneratedWeapons.Value)
{
<removed>5__7 = <>4__this.CleanupOldGeneratedWeapons(null);
if (<removed>5__7 > 0 && <>4__this.logActions.Value)
{
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Removed old generated weapons before generating a new one: " + <removed>5__7));
}
<>2__current = null;
<>1__state = 1;
return true;
}
goto IL_0139;
case 1:
<>1__state = -1;
goto IL_0139;
case 2:
<>1__state = -1;
if (!<>4__this.IsWeaponSlotEmpty(<targetSlot>5__3))
{
((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)("Could not give random weapon: target slot is still occupied: " + ((object)(InventorySlot)(ref <targetSlot>5__3)).ToString()));
return false;
}
<beforeItems>5__4 = <>4__this.CapturePlayerInventoryItems();
try
{
StaticInstance<UIManager>.Instance.InventoryUI.SpawnItemInSlot((ItemDefinition)(object)<weapon>5__2, <targetSlot>5__3, (InventoryData)null);
if (<>4__this.announcePickup.Value && (Object)(object)<backpack>5__1 != (Object)null)
{
try
{
<backpack>5__1.PlayPickupNote((ItemDefinition)(object)<weapon>5__2, (int?)null);
}
catch
{
}
}
}
catch (Exception ex)
{
<ex>5__8 = ex;
((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)("Failed to spawn random weapon " + GetWeaponName(<weapon>5__2) + " into " + ((object)(InventorySlot)(ref <targetSlot>5__3)).ToString() + ": " + <ex>5__8.Message));
return false;
}
<waitFrames>5__5 = Mathf.Clamp(<>4__this.postCreateWaitFrames.Value, 1, 60);
<i>5__9 = 0;
break;
case 3:
{
<>1__state = -1;
<i>5__9++;
break;
}
IL_0139:
<targetSlot>5__3 = <>4__this.PrepareTargetWeaponSlot();
if ((int)<targetSlot>5__3 == 9)
{
((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)"Could not give random weapon: no usable Weapon0/Weapon1 slot. Backpack may be full.");
return false;
}
<>2__current = null;
<>1__state = 2;
return true;
}
if (<i>5__9 < <waitFrames>5__5)
{
<>2__current = null;
<>1__state = 3;
return true;
}
<createdItem>5__6 = <>4__this.FindNewInventoryItem(<beforeItems>5__4, <weapon>5__2);
if ((Object)(object)<createdItem>5__6 == (Object)null)
{
<createdItem>5__6 = <>4__this.GetItemInWeaponSlot(<targetSlot>5__3);
}
if ((Object)(object)<createdItem>5__6 != (Object)null)
{
<>4__this.MarkGeneratedWeapon(<createdItem>5__6, reason, levelSignature);
if (<>4__this.forceSelectGeneratedWeapon.Value)
{
<>4__this.TrySelectWeaponSlot(<targetSlot>5__3);
}
<appliedOilCount>5__10 = <>4__this.ApplyRandomOilsIfNeeded(<createdItem>5__6);
<appliedScrollCount>5__11 = <>4__this.ApplyRandomScrollsIfNeeded(<createdItem>5__6);
<>4__this.GrantMinimumRankForAppliedEnchantments(<createdItem>5__6, <appliedOilCount>5__10 + <appliedScrollCount>5__11);
<>4__this.TrySyncInstancedWeapon(<createdItem>5__6);
<appliedAttachmentCount>5__12 = <>4__this.ApplyRandomAttachmentsIfNeeded(<createdItem>5__6);
<>4__this.TrySyncInstancedWeapon(<createdItem>5__6);
<>4__this.NormalizeDurabilityIfNeeded(<createdItem>5__6);
<>4__this.FixDurabilityIfNeeded(<createdItem>5__6);
<>4__this.TrySyncInstancedWeapon(<createdItem>5__6);
if (<>4__this.forceSelectGeneratedWeapon.Value)
{
<>4__this.TrySelectWeaponSlot(<targetSlot>5__3);
}
if (<>4__this.cleanupOldGeneratedWeapons.Value)
{
<>4__this.CleanupOldGeneratedWeapons(<createdItem>5__6);
}
}
if (<>4__this.logActions.Value)
{
<itemInfo>5__13 = (((Object)(object)<createdItem>5__6 != (Object)null) ? <>4__this.BuildCreatedItemInfo(<createdItem>5__6) : "created item not found after SpawnItemInSlot");
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Generated random weapon by " + reason + " into " + ((object)(InventorySlot)(ref <targetSlot>5__3)).ToString() + ": " + GetWeaponName(<weapon>5__2) + " (" + <itemInfo>5__13 + ")"));
<itemInfo>5__13 = null;
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
internal static ManualLogSource Log;
internal static Plugin Instance;
private Harmony harmony;
private ConfigEntry<bool> enableMod;
private ConfigEntry<bool> randomizeOnLevelStart;
private ConfigEntry<bool> debugKeyEnabled;
private ConfigEntry<Key> debugKey;
private int lastTransitionCleanupFrame = -1;
private ConfigEntry<PreferredWeaponSlotMode> preferredWeaponSlot;
private ConfigEntry<bool> forceSelectGeneratedWeapon;
private ConfigEntry<bool> cleanupOldGeneratedWeapons;
private ConfigEntry<bool> destroyGeneratedWeaponsOnDrop;
private ConfigEntry<bool> cleanupGeneratedWeaponsBeforeLevelTransition;
private ConfigEntry<bool> gunsOnly;
private ConfigEntry<bool> excludeStartsEmpty;
private ConfigEntry<bool> requireWeaponPrefab;
private ConfigEntry<bool> requireAmmoMagazine;
private ConfigEntry<bool> requireUsableByPlayer;
private ConfigEntry<bool> enableRandomOils;
private ConfigEntry<int> minOilCount;
private ConfigEntry<int> maxOilCount;
private ConfigEntry<bool> respectEnchantmentSlots;
private ConfigEntry<bool> grantRankForAppliedOils;
private ConfigEntry<bool> enableRandomScrolls;
private ConfigEntry<float> scrollChance;
private ConfigEntry<int> minScrollCount;
private ConfigEntry<int> maxScrollCount;
private ConfigEntry<bool> enableRandomWeaponOnKill;
private ConfigEntry<float> randomWeaponOnKillCooldown;
private ConfigEntry<bool> killRewardRequiresPlayableLevel;
private ConfigEntry<bool> killRewardRequireExperienceOnKill;
private ConfigEntry<bool> logKillRewardChecks;
private float nextKillRewardAllowedTime;
private readonly HashSet<int> rewardedKillUnitIds = new HashSet<int>();
private ConfigEntry<bool> enableRandomAttachments;
private ConfigEntry<float> attachmentChance;
private ConfigEntry<int> minAttachmentCount;
private ConfigEntry<int> maxAttachmentCount;
private ConfigEntry<float> minimumDurabilityNormalized;
private ConfigEntry<bool> fixLowDurability;
private ConfigEntry<bool> announcePickup;
private ConfigEntry<bool> logActions;
private ConfigEntry<bool> logWeaponPool;
private ConfigEntry<float> autoGiveDelayAfterLevelStart;
private ConfigEntry<int> postCreateWaitFrames;
private ConfigEntry<float> sceneSignatureCheckInterval;
private readonly List<WeaponSO> cachedWeaponPool = new List<WeaponSO>();
private readonly HashSet<int> autoGivenLevelSignatures = new HashSet<int>();
private bool sawLoadingOrLevelTransition;
private bool autoGiveRoutineRunning;
private int currentLevelSignature;
private int cachedLevelSignature;
private float nextSignatureCheckTime;
private int generationCounter;
internal int CleanupGeneratedWeaponsForTransition(string reason)
{
if (!ShouldCleanupBeforeLevelTransition())
{
return 0;
}
if (lastTransitionCleanupFrame == Time.frameCount)
{
return 0;
}
lastTransitionCleanupFrame = Time.frameCount;
int num = CleanupOldGeneratedWeapons(null);
if (num > 0)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)("Removed generated weapon(s) before transition: " + num + " (" + reason + ")"));
}
}
else if (logActions != null && logActions.Value)
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogInfo((object)("Transition cleanup checked, no generated weapon found. (" + reason + ")"));
}
}
return num;
}
private void Awake()
{
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Expected O, but got Unknown
//IL_0230: Unknown result type (might be due to invalid IL or missing references)
//IL_023a: Expected O, but got Unknown
//IL_026e: Unknown result type (might be due to invalid IL or missing references)
//IL_0278: Expected O, but got Unknown
//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
//IL_03f5: Expected O, but got Unknown
//IL_041d: Unknown result type (might be due to invalid IL or missing references)
//IL_0427: Expected O, but got Unknown
//IL_04be: Unknown result type (might be due to invalid IL or missing references)
//IL_04c8: Expected O, but got Unknown
//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
//IL_04fa: Expected O, but got Unknown
//IL_0522: Unknown result type (might be due to invalid IL or missing references)
//IL_052c: Expected O, but got Unknown
//IL_0581: Unknown result type (might be due to invalid IL or missing references)
//IL_058b: Expected O, but got Unknown
//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
//IL_05bd: Expected O, but got Unknown
//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
//IL_05ef: Expected O, but got Unknown
//IL_0644: Unknown result type (might be due to invalid IL or missing references)
//IL_064e: Expected O, but got Unknown
//IL_066b: Unknown result type (might be due to invalid IL or missing references)
//IL_0675: Expected O, but got Unknown
//IL_071a: Unknown result type (might be due to invalid IL or missing references)
//IL_071f: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Instance = this;
enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "Enable this mod.");
randomizeOnLevelStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RandomizeOnLevelStart", true, "Give one random weapon when entering a non-safe-zone level.");
enableRandomWeaponOnKill = ((BaseUnityPlugin)this).Config.Bind<bool>("Kill Reward", "EnableRandomWeaponOnKill", true, "If true, killing an eligible enemy grants one generated random weapon.");
randomWeaponOnKillCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Kill Reward", "RandomWeaponOnKillCooldown", 1f, new ConfigDescription("Cooldown in seconds for random weapon rewards from enemy kills.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
killRewardRequiresPlayableLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("Kill Reward", "KillRewardRequiresPlayableLevel", true, "If true, kill rewards only trigger in playable non-safe-zone levels.");
killRewardRequireExperienceOnKill = ((BaseUnityPlugin)this).Config.Bind<bool>("Kill Reward", "KillRewardRequireExperienceOnKill", true, "If true, only units with ExperienceOnKill > 0 can trigger a random weapon reward. This avoids most props and non-enemy objects.");
logKillRewardChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("Kill Reward", "LogKillRewardChecks", false, "Log detailed kill reward checks. Keep false for normal gameplay.");
nextKillRewardAllowedTime = 0f;
announcePickup = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AnnouncePickup", true, "Show pickup notification when a random weapon is generated.");
preferredWeaponSlot = ((BaseUnityPlugin)this).Config.Bind<PreferredWeaponSlotMode>("General", "PreferredWeaponSlot", PreferredWeaponSlotMode.FirstAvailable, "Which weapon slot should receive the generated weapon.");
forceSelectGeneratedWeapon = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceSelectGeneratedWeapon", true, "Force-select the generated weapon after it is equipped.");
cleanupOldGeneratedWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CleanupOldGeneratedWeapons", true, "Remove older runtime-tagged weapons generated by this mod before generating a new one.");
cleanupGeneratedWeaponsBeforeLevelTransition = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "CleanupGeneratedWeaponsBeforeLevelTransition", true, "Remove runtime-tagged generated weapons before GameManager.SwitchLevel runs. This prevents generated weapons from entering cross-level equipment data.");
destroyGeneratedWeaponsOnDrop = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "DestroyGeneratedWeaponsOnDrop", true, "Destroy runtime-tagged generated weapons when the player tries to drop them. This prevents tag loss through ground pickup data.");
autoGiveDelayAfterLevelStart = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "AutoGiveDelayAfterLevelStart", 2f, "Delay before giving the level-start random weapon after entering a playable level.");
postCreateWaitFrames = ((BaseUnityPlugin)this).Config.Bind<int>("Timing", "PostCreateWaitFrames", 8, new ConfigDescription("Frames to wait after SpawnItemInSlot before searching for the created InventoryItem.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 60), Array.Empty<object>()));
sceneSignatureCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "SceneSignatureCheckInterval", 0.5f, new ConfigDescription("How often the mod checks the current level signature.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
debugKeyEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugKeyEnabled", false, "Enable debug key spawning.");
debugKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Debug", "DebugKey", (Key)25, "Press this key to generate one random debug weapon. This can be used repeatedly.");
logActions = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogActions", true, "Log generated weapon information.");
logWeaponPool = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogWeaponPool", false, "Log every weapon in the random pool. Keep false unless debugging.");
gunsOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Pool", "GunsOnly", true, "Exclude melee and throwable weapons.");
excludeStartsEmpty = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Pool", "ExcludeStartsEmpty", true, "Exclude weapons marked as startsEmpty.");
requireWeaponPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Pool", "RequireWeaponPrefab", true, "Only include weapons with a prefab. Recommended.");
requireAmmoMagazine = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Pool", "RequireAmmoMagazine", true, "Only include weapons with iAmmoMax > 0.");
requireUsableByPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Pool", "RequireUsableByPlayer", true, "Only include WeaponSO.usableByPlayer weapons.");
enableRandomOils = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Upgrades", "EnableRandomOils", true, "Add random oil enchantments to generated weapons.");
minOilCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MinOilCount", 1, new ConfigDescription("Minimum random oil enchantments added to generated weapons.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
maxOilCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MaxOilCount", 5, new ConfigDescription("Maximum random oil enchantments added to generated weapons.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
respectEnchantmentSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Upgrades", "RespectEnchantmentSlots", false, "If true, only add oils/scrolls when the weapon has free enchantment slots. If false, this mod can create chaos guns regardless of rank.");
grantRankForAppliedOils = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Upgrades", "GrantRankForAppliedOils", true, "If true, generated weapons gain enough XP to match the total applied enchantment count.");
enableRandomScrolls = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Upgrades", "EnableRandomScrolls", true, "Add random scroll enchantments to generated weapons.");
scrollChance = ((BaseUnityPlugin)this).Config.Bind<float>("Random Upgrades", "ScrollChance", 0.5f, new ConfigDescription("Chance for a generated weapon to receive scroll enchantments. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
minScrollCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MinScrollCount", 1, new ConfigDescription("Minimum scroll enchantments if scroll generation succeeds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
maxScrollCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MaxScrollCount", 2, new ConfigDescription("Maximum scroll enchantments if scroll generation succeeds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
enableRandomAttachments = ((BaseUnityPlugin)this).Config.Bind<bool>("Random Upgrades", "EnableRandomAttachments", true, "Add random compatible attachments to generated weapons.");
attachmentChance = ((BaseUnityPlugin)this).Config.Bind<float>("Random Upgrades", "AttachmentChance", 0.6f, new ConfigDescription("Chance for a generated weapon to receive attachments. 0 = never, 1 = always.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
minAttachmentCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MinAttachmentCount", 1, new ConfigDescription("Minimum attachments if attachment generation succeeds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>()));
maxAttachmentCount = ((BaseUnityPlugin)this).Config.Bind<int>("Random Upgrades", "MaxAttachmentCount", 2, new ConfigDescription("Maximum attachments if attachment generation succeeds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>()));
fixLowDurability = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "FixLowDurability", true, "Raise generated weapon durability to MinimumDurabilityNormalized if it is lower.");
minimumDurabilityNormalized = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "MinimumDurabilityNormalized", 0.5f, new ConfigDescription("Minimum durability for generated weapons. 0.5 = at least 50%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
SceneManager.sceneLoaded += OnSceneLoaded;
harmony = new Harmony("kumo.sulfur.random_weapon_per_level");
TryPatchHarmony();
sawLoadingOrLevelTransition = true;
currentLevelSignature = 0;
cachedLevelSignature = 0;
nextSignatureCheckTime = 0f;
generationCounter = 0;
ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
string[] obj = new string[10]
{
"Random Weapon Per Level 0.4.2 loaded. EnableMod=",
enableMod.Value.ToString(),
", RandomizeOnLevelStart=",
randomizeOnLevelStart.Value.ToString(),
", DebugKeyEnabled=",
debugKeyEnabled.Value.ToString(),
", DebugKey=",
null,
null,
null
};
Key value = debugKey.Value;
obj[7] = ((object)(Key)(ref value)).ToString();
obj[8] = ", PreferredWeaponSlot=";
obj[9] = preferredWeaponSlot.Value.ToString();
logger.LogInfo((object)string.Concat(obj));
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
sawLoadingOrLevelTransition = true;
currentLevelSignature = 0;
cachedLevelSignature = 0;
nextSignatureCheckTime = 0f;
rewardedKillUnitIds.Clear();
if (logActions != null && logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: " + ((Scene)(ref scene)).name + ". Auto-give transition armed."));
}
}
private void TryPatchHarmony()
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Expected O, but got Unknown
try
{
MethodInfo methodInfo = AccessTools.Method(typeof(InventoryItem), "DropFromPlayer", (Type[])null, (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(typeof(GeneratedWeaponDropFromPlayerHook), "Prefix", (Type[])null, (Type[])null);
if (methodInfo != null && methodInfo2 != null)
{
harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched InventoryItem.DropFromPlayer.");
}
else
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not patch InventoryItem.DropFromPlayer.");
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch InventoryItem.DropFromPlayer: " + ex.Message));
}
TryPatchLevelTransitionMethods();
TryPatchNextLevelTrigger();
TryPatchAmuletHelper();
TryPatchUnitDie();
}
private void TryPatchUnitDie()
{
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Expected O, but got Unknown
try
{
Type type = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Units.Unit");
if (type == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Unit type for kill reward patch.");
return;
}
MethodInfo methodInfo = AccessTools.Method(typeof(UnitDieRandomWeaponRewardHook), "Prefix", (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find UnitDieRandomWeaponRewardHook.Prefix.");
return;
}
int num = 0;
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo2 in methods)
{
if (!(methodInfo2 == null) && !(methodInfo2.Name != "Die"))
{
try
{
harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
num++;
((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched Unit.Die for random weapon on kill. Params=" + methodInfo2.GetParameters().Length));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch Unit.Die overload: " + ex.Message));
}
}
}
if (num == 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"No Unit.Die method was patched.");
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch Unit.Die for kill reward: " + ex2.Message));
}
}
internal void TryRewardRandomWeaponOnEnemyDeath(object unit)
{
if (enableRandomWeaponOnKill == null || !enableRandomWeaponOnKill.Value || unit == null || (killRewardRequiresPlayableLevel.Value && !IsInPlayableLevel()))
{
return;
}
if (Time.unscaledTime < nextKillRewardAllowedTime)
{
if (logKillRewardChecks.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Kill reward skipped by cooldown.");
}
}
else
{
if (!ShouldRewardForDeadUnit(unit))
{
return;
}
int stableRuntimeUnitId = GetStableRuntimeUnitId(unit);
if (stableRuntimeUnitId == 0)
{
return;
}
if (!rewardedKillUnitIds.Add(stableRuntimeUnitId))
{
if (logKillRewardChecks.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Kill reward skipped: unit already rewarded. id=" + stableRuntimeUnitId));
}
return;
}
nextKillRewardAllowedTime = Time.unscaledTime + Mathf.Max(0f, randomWeaponOnKillCooldown.Value);
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Kill reward triggered. Generating random weapon.");
}
((MonoBehaviour)this).StartCoroutine(GiveRandomWeaponRoutine("enemy kill reward", currentLevelSignature));
}
}
private bool ShouldRewardForDeadUnit(object unit)
{
if (unit == null)
{
return false;
}
Component val = (Component)((unit is Component) ? unit : null);
if ((Object)(object)val == (Object)null)
{
return false;
}
if ((Object)(object)val.gameObject == (Object)null)
{
return false;
}
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance != (Object)null)
{
if ((Object)(object)instance.PlayerObject != (Object)null && (Object)(object)val.gameObject == (Object)(object)instance.PlayerObject)
{
return false;
}
if ((Object)(object)instance.PlayerUnit != (Object)null && unit == instance.PlayerUnit)
{
return false;
}
}
}
catch
{
}
if (killRewardRequireExperienceOnKill.Value)
{
if (!TryReadFloatMember(unit, "ExperienceOnKill", out var value))
{
if (logKillRewardChecks.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Kill reward skipped: could not read ExperienceOnKill from " + ((Object)val.gameObject).name));
}
return false;
}
if (value <= 0f)
{
if (logKillRewardChecks.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Kill reward skipped: ExperienceOnKill <= 0 for " + ((Object)val.gameObject).name));
}
return false;
}
}
return true;
}
private bool TryReadFloatMember(object target, string memberName, out float value)
{
value = 0f;
if (target == null)
{
return false;
}
Type type = target.GetType();
try
{
PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null)
{
object value2 = property.GetValue(target, null);
if (TryConvertToFloat(value2, out value))
{
return true;
}
}
}
catch
{
}
try
{
FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
object value3 = field.GetValue(target);
if (TryConvertToFloat(value3, out value))
{
return true;
}
}
}
catch
{
}
return false;
}
private bool TryConvertToFloat(object raw, out float value)
{
value = 0f;
if (raw == null)
{
return false;
}
try
{
if (raw is float)
{
value = (float)raw;
return true;
}
if (raw is int)
{
value = (int)raw;
return true;
}
if (raw is double)
{
value = (float)(double)raw;
return true;
}
value = Convert.ToSingle(raw);
return true;
}
catch
{
return false;
}
}
private int GetStableRuntimeUnitId(object unit)
{
if (unit == null)
{
return 0;
}
try
{
Component val = (Component)((unit is Component) ? unit : null);
if ((Object)(object)val != (Object)null)
{
return ((Object)val).GetInstanceID();
}
}
catch
{
}
try
{
return unit.GetHashCode();
}
catch
{
return 0;
}
}
private void TryPatchAmuletHelper()
{
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Expected O, but got Unknown
try
{
Type type = AccessTools.TypeByName("PerfectRandom.Sulfur.Gameplay.Items.AmuletHelper");
if (type == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find AmuletHelper type.");
return;
}
MethodInfo methodInfo = AccessTools.Method(type, "DoneChanneling", (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find AmuletHelper.DoneChanneling.");
return;
}
MethodInfo methodInfo2 = AccessTools.Method(typeof(AmuletTeleportCleanupHook), "Prefix", (Type[])null, (Type[])null);
if (methodInfo2 == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find AmuletTeleportCleanupHook.Prefix.");
return;
}
harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched AmuletHelper.DoneChanneling.");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch AmuletHelper.DoneChanneling: " + ex.Message));
}
}
private void TryPatchNextLevelTrigger()
{
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Expected O, but got Unknown
try
{
Type type = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.LevelGeneration.NextLevelTrigger");
if (type == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find NextLevelTrigger type.");
return;
}
MethodInfo methodInfo = AccessTools.Method(type, "MakeTransition", (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find NextLevelTrigger.MakeTransition.");
return;
}
MethodInfo methodInfo2 = AccessTools.Method(typeof(NextLevelTriggerCleanupHook), "Prefix", (Type[])null, (Type[])null);
if (methodInfo2 == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find NextLevelTriggerCleanupHook.Prefix.");
return;
}
harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched NextLevelTrigger.MakeTransition.");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch NextLevelTrigger.MakeTransition: " + ex.Message));
}
}
private void TryPatchLevelTransitionMethods()
{
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Expected O, but got Unknown
try
{
MethodInfo methodInfo = AccessTools.Method(typeof(LevelTransitionCleanupHook), "Prefix", (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find LevelTransitionCleanupHook.Prefix.");
return;
}
int num = 0;
HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
MethodInfo[] methods = typeof(GameManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo2 in methods)
{
if (!(methodInfo2 == null) && IsLikelyLevelTransitionMethod(methodInfo2) && !hashSet.Contains(methodInfo2))
{
try
{
harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
hashSet.Add(methodInfo2);
num++;
((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched level transition method: GameManager." + methodInfo2.Name + "(" + methodInfo2.GetParameters().Length + " params)"));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch GameManager." + methodInfo2.Name + ": " + ex.Message));
}
}
}
if (num == 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"No likely GameManager level transition methods were patched.");
}
else
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Level transition cleanup patched methods: " + num));
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch level transition cleanup: " + ex2.Message));
}
}
private bool IsLikelyLevelTransitionMethod(MethodInfo method)
{
string name = method.Name;
if (string.IsNullOrEmpty(name))
{
return false;
}
switch (name)
{
case "GoToLevel":
return true;
case "GoToChurchHub":
return true;
case "GoToCarHub":
return true;
case "CompleteLevel":
return true;
case "SwitchLevel":
return true;
case "GoToNextLevel":
return true;
case "GoToPreviousLevel":
return true;
case "ReturnToChurch":
return true;
case "ReturnToChurchHub":
return true;
case "ReturnToHub":
return true;
case "LeaveLevel":
return true;
case "ExitLevel":
return true;
default:
if (name.Contains("GoTo") && name.Contains("Level"))
{
return true;
}
if (name.Contains("Switch") && name.Contains("Level"))
{
return true;
}
if (name.Contains("Load") && name.Contains("Level"))
{
return true;
}
if (name.Contains("Next") && name.Contains("Level"))
{
return true;
}
if (name.Contains("Return") && (name.Contains("Church") || name.Contains("Hub")))
{
return true;
}
return false;
}
}
private void Update()
{
if (enableMod.Value)
{
HandleDebugKey();
HandleLevelSignatureAutoGive();
}
}
internal bool ShouldCleanupBeforeLevelTransition()
{
return cleanupGeneratedWeaponsBeforeLevelTransition != null && cleanupGeneratedWeaponsBeforeLevelTransition.Value;
}
internal bool ShouldDestroyGeneratedWeaponsOnDrop()
{
return destroyGeneratedWeaponsOnDrop != null && destroyGeneratedWeaponsOnDrop.Value;
}
internal static bool IsGeneratedWeapon(InventoryItem item)
{
if ((Object)(object)item == (Object)null)
{
return false;
}
try
{
return (Object)(object)((Component)item).GetComponent<RandomWeaponMarker>() != (Object)null;
}
catch
{
return false;
}
}
internal static void RemoveGeneratedWeaponSafely(InventoryItem item, string reason)
{
if ((Object)(object)item == (Object)null)
{
return;
}
try
{
item.GetRemovedByExternalFactor(reason);
}
catch (Exception ex)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogWarning((object)("Failed to remove generated weapon: " + ex.Message));
}
}
}
private void HandleDebugKey()
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Invalid comparison between Unknown and I4
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
if (!debugKeyEnabled.Value)
{
return;
}
Keyboard current = Keyboard.current;
if (current == null)
{
return;
}
Key value = debugKey.Value;
if ((int)value == 0)
{
return;
}
try
{
if (((ButtonControl)current[value]).wasPressedThisFrame)
{
((MonoBehaviour)this).StartCoroutine(GiveRandomWeaponRoutine("debug key " + ((object)(Key)(ref value)).ToString(), 0));
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to read debug key " + ((object)(Key)(ref value)).ToString() + ": " + ex.Message));
}
}
private void HandleLevelSignatureAutoGive()
{
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Invalid comparison between Unknown and I4
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Invalid comparison between Unknown and I4
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Invalid comparison between Unknown and I4
if (!randomizeOnLevelStart.Value)
{
return;
}
GameManager instance;
try
{
instance = StaticInstance<GameManager>.Instance;
}
catch
{
return;
}
if ((Object)(object)instance == (Object)null)
{
return;
}
if (instance.InSafeZone)
{
ResetAutoGiveMemoryForSafeZone();
}
else if ((int)instance.gameState == 1 || (int)instance.gameState == 0)
{
sawLoadingOrLevelTransition = true;
currentLevelSignature = 0;
cachedLevelSignature = 0;
nextSignatureCheckTime = 0f;
}
else
{
if ((int)instance.gameState != 3 || !IsInPlayableLevel())
{
return;
}
int num = GetCachedLevelSignature();
if (num != 0)
{
if (currentLevelSignature == 0)
{
currentLevelSignature = num;
}
bool flag = sawLoadingOrLevelTransition && currentLevelSignature != num;
bool flag2 = sawLoadingOrLevelTransition && currentLevelSignature == num;
if (flag || flag2)
{
currentLevelSignature = num;
sawLoadingOrLevelTransition = false;
TryStartAutoGiveForSignature(num, "level signature");
}
}
}
}
private void ResetAutoGiveMemoryForSafeZone()
{
sawLoadingOrLevelTransition = true;
currentLevelSignature = 0;
cachedLevelSignature = 0;
nextSignatureCheckTime = 0f;
rewardedKillUnitIds.Clear();
nextKillRewardAllowedTime = 0f;
autoGivenLevelSignatures.Clear();
}
private void TryStartAutoGiveForSignature(int signature, string reason)
{
if (signature != 0 && !autoGiveRoutineRunning && !autoGivenLevelSignatures.Contains(signature))
{
autoGivenLevelSignatures.Add(signature);
((MonoBehaviour)this).StartCoroutine(AutoGiveForLevelSignature(signature, reason));
}
}
[IteratorStateMachine(typeof(<AutoGiveForLevelSignature>d__78))]
private IEnumerator AutoGiveForLevelSignature(int signature, string reason)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <AutoGiveForLevelSignature>d__78(0)
{
<>4__this = this,
signature = signature,
reason = reason
};
}
private bool IsInPlayableLevel()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Invalid comparison between Unknown and I4
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
return false;
}
if ((int)instance.gameState != 3)
{
return false;
}
if (instance.InSafeZone)
{
return false;
}
if ((Object)(object)instance.PlayerUnit == (Object)null)
{
return false;
}
if ((Object)(object)StaticInstance<UIManager>.Instance == (Object)null)
{
return false;
}
if ((Object)(object)StaticInstance<UIManager>.Instance.PlayerBackpackGrid == (Object)null)
{
return false;
}
if ((Object)(object)StaticInstance<UIManager>.Instance.InventoryUI == (Object)null)
{
return false;
}
return true;
}
catch
{
return false;
}
}
private int GetCachedLevelSignature()
{
float num = Mathf.Clamp(sceneSignatureCheckInterval.Value, 0.1f, 5f);
if (cachedLevelSignature != 0 && Time.unscaledTime < nextSignatureCheckTime)
{
return cachedLevelSignature;
}
cachedLevelSignature = ComputeLevelSignature();
nextSignatureCheckTime = Time.unscaledTime + num;
return cachedLevelSignature;
}
private int ComputeLevelSignature()
{
//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)
try
{
int num = 17;
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance != (Object)null)
{
num = num * 31 + instance.InSafeZone.GetHashCode();
}
num = num * 31 + SceneManager.sceneCount;
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene sceneAt = SceneManager.GetSceneAt(i);
if (!((Scene)(ref sceneAt)).IsValid() || !((Scene)(ref sceneAt)).isLoaded)
{
continue;
}
num = num * 31 + ((Scene)(ref sceneAt)).name.GetHashCode();
GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
num = num * 31 + rootGameObjects.Length;
foreach (GameObject val in rootGameObjects)
{
if (!((Object)(object)val == (Object)null))
{
num = num * 31 + ((Object)val).name.GetHashCode();
num = num * 31 + val.activeSelf.GetHashCode();
}
}
}
return num;
}
catch
{
return 0;
}
}
[IteratorStateMachine(typeof(<GiveRandomWeaponRoutine>d__82))]
private IEnumerator GiveRandomWeaponRoutine(string reason, int levelSignature)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <GiveRandomWeaponRoutine>d__82(0)
{
<>4__this = this,
reason = reason,
levelSignature = levelSignature
};
}
private InventorySlot PrepareTargetWeaponSlot()
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: 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_004c: Unknown result type (might be due to invalid IL or missing references)
List<InventorySlot> preferredWeaponSlotOrder = GetPreferredWeaponSlotOrder();
foreach (InventorySlot item in preferredWeaponSlotOrder)
{
if (TryPrepareWeaponSlot(item))
{
return item;
}
}
return (InventorySlot)9;
}
private List<InventorySlot> GetPreferredWeaponSlotOrder()
{
List<InventorySlot> list = new List<InventorySlot>();
switch (preferredWeaponSlot.Value)
{
case PreferredWeaponSlotMode.Weapon0:
list.Add((InventorySlot)4);
list.Add((InventorySlot)5);
return list;
case PreferredWeaponSlotMode.Weapon1:
list.Add((InventorySlot)5);
list.Add((InventorySlot)4);
return list;
case PreferredWeaponSlotMode.Random:
if (Random.value < 0.5f)
{
list.Add((InventorySlot)4);
list.Add((InventorySlot)5);
}
else
{
list.Add((InventorySlot)5);
list.Add((InventorySlot)4);
}
return list;
default:
list.Add((InventorySlot)4);
list.Add((InventorySlot)5);
return list;
}
}
private bool TryPrepareWeaponSlot(InventorySlot slot)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
PaperdollSlot paperdollSlot = GetPaperdollSlot(slot);
if ((Object)(object)paperdollSlot == (Object)null)
{
return false;
}
InventoryItem itemInSlot = paperdollSlot.itemInSlot;
if ((Object)(object)itemInSlot == (Object)null)
{
return true;
}
if ((Object)(object)((Component)itemInSlot).GetComponent<RandomWeaponMarker>() != (Object)null)
{
try
{
itemInSlot.GetRemovedByExternalFactor("Random Weapon Per Level replacing generated weapon");
return true;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to remove old generated weapon from " + ((object)(InventorySlot)(ref slot)).ToString() + ": " + ex.Message));
return false;
}
}
if (!CanMoveItemToBackpack(itemInSlot))
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Cannot move existing weapon to backpack, skipping slot " + ((object)(InventorySlot)(ref slot)).ToString() + ": " + itemInSlot.itemDefinition.displayName));
}
return false;
}
try
{
itemInSlot.TryMoveToPlayerInventory();
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to move existing weapon to backpack from " + ((object)(InventorySlot)(ref slot)).ToString() + ": " + ex2.Message));
return false;
}
return true;
}
private bool IsWeaponSlotEmpty(InventorySlot slot)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
InventoryItem itemInWeaponSlot = GetItemInWeaponSlot(slot);
return (Object)(object)itemInWeaponSlot == (Object)null;
}
private InventoryItem GetItemInWeaponSlot(InventorySlot slot)
{
//IL_0003: 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_0053: Unknown result type (might be due to invalid IL or missing references)
try
{
PaperdollSlot paperdollSlot = GetPaperdollSlot(slot);
if ((Object)(object)paperdollSlot != (Object)null)
{
return paperdollSlot.itemInSlot;
}
}
catch
{
}
try
{
EquipmentManager equipmentManager = GetEquipmentManager();
if ((Object)(object)equipmentManager != (Object)null && equipmentManager.EquippedItems.ContainsKey(slot))
{
return equipmentManager.EquippedItems[slot];
}
}
catch
{
}
return null;
}
private PaperdollSlot GetPaperdollSlot(InventorySlot slot)
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
try
{
UIManager instance = StaticInstance<UIManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
return null;
}
if ((Object)(object)instance.Paperdoll == (Object)null)
{
return null;
}
return instance.Paperdoll.GetSlot(slot);
}
catch
{
return null;
}
}
private bool CanMoveItemToBackpack(InventoryItem item)
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: 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_003d: 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_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)item == (Object)null)
{
return false;
}
try
{
ItemGrid playerBackpackGrid = GetPlayerBackpackGrid();
if ((Object)(object)playerBackpackGrid == (Object)null)
{
return false;
}
Vector2Int inventorySize = item.InventorySize;
Vector2Int possibleSpace = playerBackpackGrid.GetPossibleSpace(inventorySize, false, true);
if (((Vector2Int)(ref possibleSpace)).x >= 0 && ((Vector2Int)(ref possibleSpace)).y >= 0)
{
return true;
}
Vector2Int val = default(Vector2Int);
((Vector2Int)(ref val))..ctor(((Vector2Int)(ref inventorySize)).y, ((Vector2Int)(ref inventorySize)).x);
Vector2Int possibleSpace2 = playerBackpackGrid.GetPossibleSpace(val, false, true);
return ((Vector2Int)(ref possibleSpace2)).x >= 0 && ((Vector2Int)(ref possibleSpace2)).y >= 0;
}
catch
{
return false;
}
}
private void MarkGeneratedWeapon(InventoryItem item, string reason, int levelSignature)
{
if (!((Object)(object)item == (Object)null))
{
RandomWeaponMarker randomWeaponMarker = ((Component)item).GetComponent<RandomWeaponMarker>();
if ((Object)(object)randomWeaponMarker == (Object)null)
{
randomWeaponMarker = ((Component)item).gameObject.AddComponent<RandomWeaponMarker>();
}
generationCounter++;
randomWeaponMarker.generationId = generationCounter;
randomWeaponMarker.levelSignature = levelSignature;
randomWeaponMarker.reason = reason;
randomWeaponMarker.weaponName = (((Object)(object)item.itemDefinition != (Object)null) ? item.itemDefinition.displayName : "Unknown");
}
}
internal int CleanupOldGeneratedWeapons(InventoryItem keepItem)
{
int num = 0;
try
{
RandomWeaponMarker[] array = Resources.FindObjectsOfTypeAll<RandomWeaponMarker>();
RandomWeaponMarker[] array2 = array;
foreach (RandomWeaponMarker randomWeaponMarker in array2)
{
if ((Object)(object)randomWeaponMarker == (Object)null)
{
continue;
}
InventoryItem component = ((Component)randomWeaponMarker).GetComponent<InventoryItem>();
if (!((Object)(object)component == (Object)null) && (!((Object)(object)keepItem != (Object)null) || !((Object)(object)component == (Object)(object)keepItem)))
{
try
{
component.GetRemovedByExternalFactor("Random Weapon Per Level cleanup");
num++;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to remove generated weapon: " + ex.Message));
}
}
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to cleanup generated weapons: " + ex2.Message));
}
return num;
}
private void TrySelectWeaponSlot(InventorySlot slot)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
try
{
EquipmentManager equipmentManager = GetEquipmentManager();
if (!((Object)(object)equipmentManager == (Object)null))
{
equipmentManager.ChangeWeapon(slot, true);
}
}
catch (Exception ex)
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to select generated weapon slot " + ((object)(InventorySlot)(ref slot)).ToString() + ": " + ex.Message));
}
}
}
private ItemGrid GetPlayerBackpackGrid()
{
try
{
UIManager instance = StaticInstance<UIManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
return null;
}
return instance.PlayerBackpackGrid;
}
catch
{
return null;
}
}
private HashSet<InventoryItem> CapturePlayerInventoryItems()
{
HashSet<InventoryItem> hashSet = new HashSet<InventoryItem>();
try
{
ItemGrid playerBackpackGrid = GetPlayerBackpackGrid();
if ((Object)(object)playerBackpackGrid != (Object)null)
{
foreach (InventoryItem item in playerBackpackGrid.AllItems())
{
if ((Object)(object)item != (Object)null)
{
hashSet.Add(item);
}
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to capture backpack items: " + ex.Message));
}
try
{
EquipmentManager equipmentManager = GetEquipmentManager();
if ((Object)(object)equipmentManager != (Object)null)
{
foreach (InventoryItem value in equipmentManager.EquippedItems.Values)
{
if ((Object)(object)value != (Object)null)
{
hashSet.Add(value);
}
}
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to capture equipped items: " + ex2.Message));
}
return hashSet;
}
private List<InventoryItem> GetAllPlayerItemsSnapshot()
{
HashSet<InventoryItem> collection = CapturePlayerInventoryItems();
return new List<InventoryItem>(collection);
}
private InventoryItem FindNewInventoryItem(HashSet<InventoryItem> beforeItems, WeaponSO weapon)
{
if ((Object)(object)weapon == (Object)null)
{
return null;
}
List<InventoryItem> allPlayerItemsSnapshot = GetAllPlayerItemsSnapshot();
foreach (InventoryItem item in allPlayerItemsSnapshot)
{
if ((Object)(object)item == (Object)null || (beforeItems != null && beforeItems.Contains(item)) || !((Object)(object)item.itemDefinition == (Object)(object)weapon))
{
continue;
}
return item;
}
return null;
}
private EquipmentManager GetEquipmentManager()
{
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
return null;
}
if ((Object)(object)instance.EquipmentManager != (Object)null)
{
return instance.EquipmentManager;
}
if ((Object)(object)instance.PlayerScript != (Object)null)
{
return instance.PlayerScript.equipmentManager;
}
return null;
}
catch
{
return null;
}
}
private int ApplyRandomOilsIfNeeded(InventoryItem item)
{
//IL_016b: Unknown result type (might be due to invalid IL or missing references)
//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
if (!enableRandomOils.Value)
{
return 0;
}
if ((Object)(object)item == (Object)null)
{
return 0;
}
ItemDefinition itemDefinition = item.itemDefinition;
WeaponSO val = (WeaponSO)(object)((itemDefinition is WeaponSO) ? itemDefinition : null);
if ((Object)(object)val == (Object)null)
{
return 0;
}
if (!val.TypeIsEnchantable)
{
return 0;
}
int num = Mathf.Clamp(minOilCount.Value, 1, 5);
int max = Mathf.Clamp(maxOilCount.Value, num, 5);
int num2 = PickWeightedOilCount(num, max);
LootTable oilLootTable = GetOilLootTable();
if ((Object)(object)oilLootTable == (Object)null || oilLootTable.entries == null || oilLootTable.entries.Count == 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not apply random oils: oil loot table is empty.");
return 0;
}
HashSet<ItemId> hashSet = new HashSet<ItemId>();
int num3 = 0;
int num4 = 0;
int num5 = num2 * 30;
while (num3 < num2 && num4 < num5)
{
num4++;
ItemDefinition val2 = null;
try
{
val2 = oilLootTable.SelectOneItem();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to select random oil: " + ex.Message));
break;
}
if ((Object)(object)val2 == (Object)null || !((EnchantmentId)(ref val2.appliesEnchantment)).IsValid || hashSet.Contains(val2.id) || IsEnchantmentAlreadyApplied(item, val2) || (respectEnchantmentSlots.Value && !item.IsEnchantmentCompatible(val2)))
{
continue;
}
try
{
item.AddEnchantment(val2, false);
hashSet.Add(val2.id);
num3++;
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied random oil to " + item.itemDefinition.displayName + ": " + val2.displayName));
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to apply random oil " + val2.displayName + " to " + item.itemDefinition.displayName + ": " + ex2.Message));
}
}
return num3;
}
private int PickWeightedOilCount(int min, int max)
{
min = Mathf.Clamp(min, 1, 5);
max = Mathf.Clamp(max, min, 5);
float num = 0f;
for (int i = min; i <= max; i++)
{
num += GetOilCountWeight(i);
}
if (num <= 0f)
{
return Random.Range(min, max + 1);
}
float num2 = Random.value * num;
float num3 = 0f;
for (int j = min; j <= max; j++)
{
num3 += GetOilCountWeight(j);
if (num2 <= num3)
{
return j;
}
}
return max;
}
private float GetOilCountWeight(int count)
{
return count switch
{
1 => 5f,
2 => 4f,
3 => 3.5f,
4 => 3f,
5 => 2.5f,
_ => 1f,
};
}
private int ApplyRandomScrollsIfNeeded(InventoryItem item)
{
//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
//IL_022b: Unknown result type (might be due to invalid IL or missing references)
if (!enableRandomScrolls.Value)
{
return 0;
}
if ((Object)(object)item == (Object)null)
{
return 0;
}
ItemDefinition itemDefinition = item.itemDefinition;
WeaponSO val = (WeaponSO)(object)((itemDefinition is WeaponSO) ? itemDefinition : null);
if ((Object)(object)val == (Object)null)
{
return 0;
}
if (!val.TypeIsEnchantable)
{
return 0;
}
float num = Mathf.Clamp01(scrollChance.Value);
if (Random.value > num)
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Random scroll roll skipped for " + item.itemDefinition.displayName + "."));
}
return 0;
}
LootTable scrollLootTable = GetScrollLootTable();
if ((Object)(object)scrollLootTable == (Object)null || scrollLootTable.entries == null || scrollLootTable.entries.Count == 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not apply random scrolls: scroll loot table is empty.");
return 0;
}
int num2 = Mathf.Clamp(minScrollCount.Value, 1, 5);
int num3 = Mathf.Clamp(maxScrollCount.Value, num2, 5);
int num4 = Random.Range(num2, num3 + 1);
HashSet<ItemId> hashSet = new HashSet<ItemId>();
int num5 = 0;
int num6 = 0;
int num7 = num4 * 30;
while (num5 < num4 && num6 < num7)
{
num6++;
ItemDefinition val2 = null;
try
{
val2 = scrollLootTable.SelectOneItem();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to select random scroll: " + ex.Message));
break;
}
if ((Object)(object)val2 == (Object)null || !((EnchantmentId)(ref val2.appliesEnchantment)).IsValid || hashSet.Contains(val2.id) || IsEnchantmentAlreadyApplied(item, val2) || (respectEnchantmentSlots.Value && !item.IsEnchantmentCompatible(val2)))
{
continue;
}
try
{
item.AddEnchantment(val2, false);
hashSet.Add(val2.id);
num5++;
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied random scroll to " + item.itemDefinition.displayName + ": " + val2.displayName));
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to apply random scroll " + val2.displayName + " to " + item.itemDefinition.displayName + ": " + ex2.Message));
}
}
return num5;
}
private bool IsEnchantmentAlreadyApplied(InventoryItem item, ItemDefinition enchantmentItem)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)item == (Object)null || (Object)(object)enchantmentItem == (Object)null)
{
return false;
}
if (!((EnchantmentId)(ref enchantmentItem.appliesEnchantment)).IsValid)
{
return false;
}
try
{
EnchantmentDefinition asset = AssetAccess.GetAsset(enchantmentItem.appliesEnchantment);
if ((Object)(object)asset == (Object)null)
{
return false;
}
return item.enchantments != null && item.enchantments.Contains(asset);
}
catch
{
return false;
}
}
private int ApplyRandomAttachmentsIfNeeded(InventoryItem item)
{
//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
if (!enableRandomAttachments.Value)
{
return 0;
}
if ((Object)(object)item == (Object)null)
{
return 0;
}
ItemDefinition itemDefinition = item.itemDefinition;
WeaponSO val = (WeaponSO)(object)((itemDefinition is WeaponSO) ? itemDefinition : null);
if ((Object)(object)val == (Object)null)
{
return 0;
}
float num = Mathf.Clamp01(attachmentChance.Value);
if (Random.value > num)
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Random attachment roll skipped for " + item.itemDefinition.displayName + "."));
}
return 0;
}
LootTable attachmentLootTable = GetAttachmentLootTable();
if ((Object)(object)attachmentLootTable == (Object)null || attachmentLootTable.entries == null || attachmentLootTable.entries.Count == 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not apply random attachments: attachment loot table is empty.");
return 0;
}
int num2 = Mathf.Clamp(minAttachmentCount.Value, 1, 4);
int num3 = Mathf.Clamp(maxAttachmentCount.Value, num2, 4);
int num4 = Random.Range(num2, num3 + 1);
HashSet<ItemId> hashSet = new HashSet<ItemId>();
int num5 = 0;
while (num5 < num4)
{
List<WeightedAttachmentCandidate> list = BuildCompatibleAttachmentCandidates(item, attachmentLootTable, hashSet);
if (list.Count == 0)
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("No more compatible attachments for " + item.itemDefinition.displayName + ". Applied " + num5 + "/" + num4 + "."));
}
break;
}
ItemDefinition val2 = PickWeightedAttachment(list);
if ((Object)(object)val2 == (Object)null)
{
break;
}
try
{
item.AddAttachment(val2, false);
hashSet.Add(val2.id);
num5++;
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied random attachment to " + item.itemDefinition.displayName + ": " + val2.displayName));
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to apply random attachment " + val2.displayName + " to " + item.itemDefinition.displayName + ": " + ex.Message));
hashSet.Add(val2.id);
}
}
return num5;
}
private List<WeightedAttachmentCandidate> BuildCompatibleAttachmentCandidates(InventoryItem item, LootTable attachmentTable, HashSet<ItemId> usedAttachmentIds)
{
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
List<WeightedAttachmentCandidate> list = new List<WeightedAttachmentCandidate>();
if ((Object)(object)item == (Object)null)
{
return list;
}
if ((Object)(object)attachmentTable == (Object)null || attachmentTable.entries == null)
{
return list;
}
foreach (LootEntry entry in attachmentTable.entries)
{
ItemDefinition lootItem = entry.lootItem;
if ((Object)(object)lootItem == (Object)null || entry.lootWeight <= 0f || (usedAttachmentIds != null && usedAttachmentIds.Contains(lootItem.id)))
{
continue;
}
bool flag = false;
try
{
flag = item.IsAttachmentCompatible(lootItem);
}
catch (Exception ex)
{
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Attachment compatibility check failed for " + lootItem.displayName + " on " + item.itemDefinition.displayName + ": " + ex.Message));
}
flag = false;
}
if (flag)
{
list.Add(new WeightedAttachmentCandidate(lootItem, entry.lootWeight));
}
}
return list;
}
private ItemDefinition PickWeightedAttachment(List<WeightedAttachmentCandidate> candidates)
{
if (candidates == null || candidates.Count == 0)
{
return null;
}
float num = 0f;
for (int i = 0; i < candidates.Count; i++)
{
num += Mathf.Max(0f, candidates[i].Weight);
}
if (num <= 0f)
{
int index = Random.Range(0, candidates.Count);
return candidates[index].Item;
}
float num2 = Random.value * num;
float num3 = 0f;
for (int j = 0; j < candidates.Count; j++)
{
num3 += Mathf.Max(0f, candidates[j].Weight);
if (num2 <= num3)
{
return candidates[j].Item;
}
}
return candidates[candidates.Count - 1].Item;
}
private void GrantMinimumRankForAppliedEnchantments(InventoryItem item, int appliedEnchantmentCount)
{
if (!grantRankForAppliedOils.Value || (Object)(object)item == (Object)null || appliedEnchantmentCount <= 0)
{
return;
}
int num = Mathf.Clamp(appliedEnchantmentCount, 0, 5);
if (num <= 0)
{
return;
}
try
{
float experience = item.GetExperience();
float minimumExperienceForRank = GetMinimumExperienceForRank(num);
if (!(experience >= minimumExperienceForRank))
{
item.AddExperience(minimumExperienceForRank - experience);
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Raised rank for " + item.itemDefinition.displayName + " to match applied enchantments. Target rank: " + num));
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to raise weapon rank for applied enchantments: " + ex.Message));
}
}
private float GetMinimumExperienceForRank(int rank)
{
return rank switch
{
1 => 50f,
2 => 125f,
3 => 312.5f,
4 => 781.25f,
5 => 1953.125f,
_ => 0f,
};
}
private LootTable GetOilLootTable()
{
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)instance.Settings == (Object)null || (Object)(object)instance.Settings.LootSettings == (Object)null)
{
return null;
}
return instance.Settings.LootSettings.enchantmentOilLootTable;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to get oil loot table: " + ex.Message));
return null;
}
}
private LootTable GetScrollLootTable()
{
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)instance.Settings == (Object)null || (Object)(object)instance.Settings.LootSettings == (Object)null)
{
return null;
}
return instance.Settings.LootSettings.enchantmentLootTable;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to get scroll loot table: " + ex.Message));
return null;
}
}
private LootTable GetAttachmentLootTable()
{
try
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)instance.Settings == (Object)null || (Object)(object)instance.Settings.LootSettings == (Object)null)
{
return null;
}
return instance.Settings.LootSettings.attachmentLootTable;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to get attachment loot table: " + ex.Message));
return null;
}
}
private void NormalizeDurabilityIfNeeded(InventoryItem item)
{
if ((Object)(object)item == (Object)null)
{
return;
}
try
{
if (!item.HasDurability)
{
return;
}
int durabilityMax = item.DurabilityMax;
int durabilityCurrent = item.DurabilityCurrent;
if (durabilityCurrent > durabilityMax)
{
item.ModifyDurability((float)(durabilityMax - durabilityCurrent));
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Clamped durability for " + item.itemDefinition.displayName + " to max durability: " + durabilityMax));
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to normalize durability: " + ex.Message));
}
}
private void FixDurabilityIfNeeded(InventoryItem item)
{
if (!fixLowDurability.Value || (Object)(object)item == (Object)null)
{
return;
}
try
{
if (!item.HasDurability)
{
return;
}
float num = Mathf.Clamp01(minimumDurabilityNormalized.Value);
if (item.DurabilityNormalized >= num)
{
return;
}
int durabilityMax = item.DurabilityMax;
int num2 = Mathf.CeilToInt((float)durabilityMax * num);
int num3 = num2 - item.DurabilityCurrent;
if (num3 > 0)
{
item.ModifyDurability((float)num3);
if (logActions.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Raised durability for " + item.itemDefinition.displayName + " to at least " + Mathf.RoundToInt(num * 100f) + "%"));
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to fix generated weapon durability: " + ex.Message));
}
}
private void TrySyncInstancedWeapon(InventoryItem item)
{
if ((Object)(object)item == (Object)null)
{
return;
}
try
{
item.SyncWithInstancedVersion();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to sync generated weapon instance: " + ex.Message));
}
}
private WeaponSO PickRandomWeapon()
{
EnsureWeaponPool();
if (cachedWeaponPool.Count == 0)
{
return null;
}
int index = Random.Range(0, cachedWeaponPool.Count);
return cachedWeaponPool[index];
}
private void EnsureWeaponPool()
{
if (cachedWeaponPool.Count <= 0)
{
RefreshWeaponPool();
}
}
private void RefreshWeaponPool()
{
cachedWeaponPool.Clear();
WeaponSO[] array;
try
{
array = Resources.FindObjectsOfTypeAll<WeaponSO>();
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to find WeaponSO assets: " + ex.Message));
return;
}
WeaponSO[] array2 = array;
foreach (WeaponSO val in array2)
{
if (IsValidWeaponForRandomPool(val) && !cachedWeaponPool.Contains(val))
{
cachedWeaponPool.Add(val);
}
}
cachedWeaponPool.Sort((WeaponSO a, WeaponSO b) => string.Compare(GetWeaponName(a), GetWeaponName(b), StringComparison.OrdinalIgnoreCase));
((BaseUnityPlugin)this).Logger.LogInfo((object)("Random weapon pool loaded. Valid weapons: " + cachedWeaponPool.Count));
if (!logWeaponPool.Value)
{
return;
}
foreach (WeaponSO item in cachedWeaponPool)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Random pool weapon: " + GetWeaponName(item)));
}
}
private bool IsValidWeaponForRandomPool(WeaponSO weapon)
{
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Invalid comparison between Unknown and I4
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Invalid comparison between Unknown and I4
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)weapon == (Object)null)
{
return false;
}
if (requireUsableByPlayer.Value && !weapon.usableByPlayer)
{
return false;
}
if (gunsOnly.Value)
{
if (weapon.IsMelee)
{
return false;
}
if (weapon.IsThrowable)
{
return false;
}
}
if (excludeStartsEmpty.Value && weapon.startsEmpty)
{
return false;
}
if (requireAmmoMagazine.Value && weapon.iAmmoMax <= 0)
{
return false;
}
if (requireWeaponPrefab.Value && (Object)(object)((ItemDefinition)weapon).prefab == (Object)null)
{
return false;
}
if ((int)((ItemDefinition)weapon).slotType != 7)
{
return false;
}
if ((int)weapon.caliber == 0)
{
return false;
}
if ((int)weapon.projectileType == 0 && !((Object)(object)weapon.customProjectile != (Object)null))
{
return false;
}
if (string.IsNullOrWhiteSpace(((ItemDefinition)weapon).displayName))
{
return false;
}
return true;
}
private string BuildCreatedItemInfo(InventoryItem item)
{
if ((Object)(object)item == (Object)null)
{
return "null";
}
int num = 0;
try
{
num = ((item.enchantments != null) ? item.enchantments.Count : 0);
}
catch
{
num = -1;
}
int num2 = 0;
try
{
num2 = ((item.attachments != null) ? item.attachments.Count : 0);
}
catch
{
num2 = -1;
}
string text = "rank=";
try
{
text += item.GetRankLevel();
}
catch
{
text += "unknown";
}
string text2 = "durability=";
try
{
text2 = ((!item.HasDurability) ? (text2 + "none") : (text2 + item.DurabilityCurrent + "/" + item.DurabilityMax));
}
catch
{
text2 += "unknown";
}
string text3 = "marker=";
try
{
RandomWeaponMarker component = ((Component)item).GetComponent<RandomWeaponMarker>();
text3 += (((Object)(object)component != (Object)null) ? component.generationId.ToString() : "none");
}
catch
{
text3 += "unknown";
}
return "created item found, enchantments=" + num + ", attachments=" + num2 + ", " + text + ", " + text2 + ", " + text3;
}
private static string GetWeaponName(WeaponSO weapon)
{
if ((Object)(object)weapon == (Object)null)
{
return "null";
}
if (!string.IsNullOrWhiteSpace(((ItemDefinition)weapon).displayName))
{
return ((ItemDefinition)weapon).displayName;
}
return ((Object)weapon).name;
}
}
internal static class AmuletTeleportCleanupHook
{
public static void Prefix()
{
try
{
if (!((Object)(object)Plugin.Instance == (Object)null))
{
Plugin.Instance.CleanupGeneratedWeaponsForTransition("AmuletHelper.DoneChanneling");
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Failed to cleanup generated weapons in AmuletHelper.DoneChanneling: " + ex.Message));
}
}
}
}
internal static class NextLevelTriggerCleanupHook
{
public static void Prefix()
{
try
{
if (!((Object)(object)Plugin.Instance == (Object)null))
{
Plugin.Instance.CleanupGeneratedWeaponsForTransition("NextLevelTrigger.MakeTransition");
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Failed to cleanup generated weapons in NextLevelTrigger.MakeTransition: " + ex.Message));
}
}
}
}
internal static class GeneratedWeaponDropFromPlayerHook
{
public static bool Prefix(InventoryItem __instance)
{
if ((Object)(object)Plugin.Instance == (Object)null)
{
return true;
}
if (!Plugin.Instance.ShouldDestroyGeneratedWeaponsOnDrop())
{
return true;
}
if (!Plugin.IsGeneratedWeapon(__instance))
{
return true;
}
Plugin.RemoveGeneratedWeaponSafely(__instance, "Random Weapon Per Level: generated weapon dropped");
return false;
}
}
internal static class UnitDieRandomWeaponRewardHook
{
public static void Prefix(object __instance)
{
try
{
if (!((Object)(object)Plugin.Instance == (Object)null))
{
Plugin.Instance.TryRewardRandomWeaponOnEnemyDeath(__instance);
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Failed to process random weapon kill reward: " + ex.Message));
}
}
}
}
internal static class LevelTransitionCleanupHook
{
public static void Prefix(MethodBase __originalMethod)
{
try
{
if (!((Object)(object)Plugin.Instance == (Object)null))
{
string reason = ((__originalMethod != null) ? (__originalMethod.DeclaringType.Name + "." + __originalMethod.Name) : "unknown transition method");
Plugin.Instance.CleanupGeneratedWeaponsForTransition(reason);
}
}
catch (Exception ex)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Failed to cleanup generated weapons before transition: " + ex.Message));
}
}
}
}