using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using WeaponFramework;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("SpawnRateChanger")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("SpawnRateChanger")]
[assembly: AssemblyTitle("SpawnRateChanger")]
[assembly: AssemblyVersion("1.1.1.0")]
namespace SpawnRates;
internal static class FrameworkBridge
{
private const string Section = "Weights";
private static readonly Dictionary<string, ConfigEntry<int>> entries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase);
public static void Start()
{
SpawnWeights.SetProvider("Spawn Rate Changer", (Func<WeaponInfo, int>)Get, (Action<WeaponInfo, int>)Set);
WeaponRegistry.Ready += BindAll;
}
public static void Refresh()
{
SpawnWeights.Refresh();
}
private static int Get(WeaponInfo w)
{
return Entry(w)?.Value ?? (-1);
}
private static void Set(WeaponInfo w, int tickets)
{
ConfigEntry<int> val = Entry(w);
if (val != null)
{
val.Value = tickets;
}
}
private static ConfigEntry<int> Entry(WeaponInfo w)
{
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Expected O, but got Unknown
if (w == null)
{
return null;
}
if (entries.TryGetValue(w.Id, out var value))
{
return value;
}
ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config;
bool saveOnConfigSet = config.SaveOnConfigSet;
config.SaveOnConfigSet = false;
try
{
value = config.Bind<int>("Weights", Key(w), -1, new ConfigDescription(w.Name + " (" + w.Category + "). Spawn tickets: -1 = its default (" + w.DefaultTickets + "), 0 = never, up to " + 1000 + ".", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 1000), new object[0]));
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Couldn't make a weight entry for " + w.Id + " (it keeps its default): " + ex.Message));
value = null;
}
finally
{
config.SaveOnConfigSet = saveOnConfigSet;
}
entries[w.Id] = value;
if (value != null && value.Value < 0)
{
int num = OldWeight(config, w);
if (num >= 0)
{
value.Value = num;
Plugin.Log.LogInfo((object)("Carried over " + w.Name + " = " + num + " from the old per-row settings."));
}
}
return value;
}
private static string Key(WeaponInfo w)
{
string text = Clean(w.Id);
if (text.Length <= 0)
{
return "weapon " + w.Index;
}
return text;
}
private static void BindAll()
{
ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config;
bool saveOnConfigSet = config.SaveOnConfigSet;
config.SaveOnConfigSet = false;
try
{
foreach (WeaponInfo item in WeaponRegistry.All)
{
Entry(item);
}
ForgetOldLines(config);
config.Save();
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to make the weight entries: " + ex));
}
finally
{
config.SaveOnConfigSet = saveOnConfigSet;
}
Plugin.Log.LogInfo((object)("Spawn weights for " + entries.Count + " weapons in [Weights] of " + config.ConfigFilePath));
}
private static Dictionary<ConfigDefinition, string> Orphans(ConfigFile config)
{
PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");
if ((object)propertyInfo == null)
{
return null;
}
return propertyInfo.GetValue(config, null) as Dictionary<ConfigDefinition, string>;
}
private static int OldWeight(ConfigFile config, WeaponInfo w)
{
try
{
Dictionary<ConfigDefinition, string> dictionary = Orphans(config);
if (dictionary == null)
{
return -1;
}
string b = Clean(w.MenuLabel ?? w.OriginalName);
foreach (KeyValuePair<ConfigDefinition, string> item in dictionary)
{
if (!(item.Key.Section == "Weights") && string.Equals(Clean(item.Key.Key), b, StringComparison.OrdinalIgnoreCase) && int.TryParse(item.Value.Trim(), out var result) && result >= 0)
{
dictionary.Remove(item.Key);
return Math.Min(result, 1000);
}
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Couldn't read the old settings: " + ex.Message));
}
return -1;
}
private static void ForgetOldLines(ConfigFile config)
{
try
{
Dictionary<ConfigDefinition, string> dictionary = Orphans(config);
if (dictionary == null || dictionary.Count == 0)
{
return;
}
HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (WeaponInfo item in WeaponRegistry.All)
{
if (entries.TryGetValue(item.Id, out var value) && value != null)
{
hashSet.Add(Clean(item.MenuLabel ?? item.OriginalName));
}
}
List<ConfigDefinition> list = new List<ConfigDefinition>();
foreach (KeyValuePair<ConfigDefinition, string> item2 in dictionary)
{
if (item2.Key.Section != "Weights" && hashSet.Contains(Clean(item2.Key.Key)))
{
list.Add(item2.Key);
}
}
foreach (ConfigDefinition item3 in list)
{
dictionary.Remove(item3);
}
if (list.Count > 0)
{
Plugin.Log.LogInfo((object)("Removed " + list.Count + " old per-row lines from the config (the weights are under [Weights] now)."));
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Couldn't tidy the old settings: " + ex.Message));
}
}
private static string Clean(string s)
{
if (s == null)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
foreach (char value in s)
{
if ("=\n\t\r\\\"'[]".IndexOf(value) < 0)
{
stringBuilder.Append(value);
}
}
return stringBuilder.ToString().Trim();
}
}
[BepInPlugin("stickfight.spawnratechanger", "Spawn Rate Changer", "1.1.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string Guid = "stickfight.spawnratechanger";
public const string Name = "Spawn Rate Changer";
public const string Version = "1.1.1";
private const string FrameworkGuid = "stickfight.weaponframework";
internal static ManualLogSource Log;
internal static Plugin Instance;
internal static bool WithFramework;
private FileSystemWatcher watcher;
private volatile bool fileChanged;
private void Awake()
{
//IL_0038: 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_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Expected O, but got Unknown
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Instance = this;
WithFramework = Chainloader.PluginInfos.ContainsKey("stickfight.weaponframework");
if (WithFramework)
{
FrameworkBridge.Start();
}
else
{
new Harmony("stickfight.spawnratechanger").PatchAll(typeof(Plugin).Assembly);
}
GameObject val = new GameObject("SpawnRateChangerManager");
Object.DontDestroyOnLoad((Object)val);
((Object)val).hideFlags = (HideFlags)61;
val.AddComponent<ReloadPoller>();
try
{
watcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
watcher.Changed += delegate
{
fileChanged = true;
};
watcher.Created += delegate
{
fileChanged = true;
};
watcher.Renamed += delegate
{
fileChanged = true;
};
watcher.EnableRaisingEvents = true;
}
catch (Exception ex)
{
Log.LogWarning((object)("Can't watch the config file; edits take effect after a restart. " + ex.Message));
}
Log.LogInfo((object)("Spawn Rate Changer 1.1.1 loaded" + (WithFramework ? " (spawn weights for Weapon Framework)." : ".")));
}
internal bool TakeFileChanged()
{
if (!fileChanged)
{
return false;
}
fileChanged = false;
return true;
}
}
internal class ReloadPoller : MonoBehaviour
{
private const float Delay = 0.5f;
private float reloadAt = -1f;
private void Update()
{
Plugin instance = Plugin.Instance;
if ((Object)(object)instance == (Object)null)
{
return;
}
if (instance.TakeFileChanged())
{
reloadAt = Time.realtimeSinceStartup + 0.5f;
}
if (!(reloadAt < 0f) && !(Time.realtimeSinceStartup < reloadAt))
{
reloadAt = -1f;
try
{
((BaseUnityPlugin)instance).Config.Reload();
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Couldn't read the config yet, retrying: " + ex.Message));
reloadAt = Time.realtimeSinceStartup + 0.5f;
return;
}
Plugin.Log.LogInfo((object)"Config reloaded.");
if (Plugin.WithFramework)
{
FrameworkBridge.Refresh();
}
else
{
Rates.Rebuild();
}
}
}
}
[HarmonyPatch]
internal static class Rates
{
private const int MaxWeight = 1000;
private static readonly FieldRef<WeaponSelectionHandler, List<byte>> s_Bag = AccessTools.FieldRefAccess<WeaponSelectionHandler, List<byte>>("m_WeaponRaritiesArray");
private static readonly FieldRef<WeaponSelectionHandler, List<SingleWeaponUI>> s_Weapons = AccessTools.FieldRefAccess<WeaponSelectionHandler, List<SingleWeaponUI>>("m_Weapons");
private static readonly MethodInfo s_IsActive = AccessTools.Method(typeof(WeaponSelectionHandler), "GetActiveStateForWeapon", (Type[])null, (Type[])null);
private static readonly MethodInfo s_Generate = AccessTools.Method(typeof(WeaponSelectionHandler), "GenerateWeaponRarityArray", (Type[])null, (Type[])null);
private static readonly Dictionary<SingleWeaponUI, ConfigEntry<int>> s_Entries = new Dictionary<SingleWeaponUI, ConfigEntry<int>>();
private static WeaponSelectionHandler s_Handler;
private static bool s_LastBuiltOnline;
private static string s_LastOdds;
[HarmonyPostfix]
[HarmonyPriority(0)]
[HarmonyPatch(typeof(WeaponSelectionHandler), "InitWeaponList")]
private static void AfterInit(WeaponSelectionHandler __instance)
{
//IL_011a: Unknown result type (might be due to invalid IL or missing references)
//IL_0124: Expected O, but got Unknown
s_Handler = __instance;
s_Entries.Clear();
ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config;
bool saveOnConfigSet = config.SaveOnConfigSet;
config.SaveOnConfigSet = false;
try
{
HashSet<string> hashSet = new HashSet<string>();
foreach (SingleWeaponUI item in s_Weapons.Invoke(__instance))
{
string text = Clean(item.Category);
string text2 = Clean(item.WeaponName);
if (text.Length == 0)
{
text = "Other";
}
if (text2.Length == 0)
{
text2 = "Weapon " + item.WeaponIndex;
}
if (!hashSet.Add(text + "\n" + text2))
{
text2 = text2 + " " + item.WeaponIndex;
hashSet.Add(text + "\n" + text2);
}
s_Entries[item] = config.Bind<int>(text, text2, -1, new ConfigDescription("Spawn weight (tickets in the draw; bigger = more common). -1 = unchanged (game: " + item.Rarity + ", other mods may change it), 0 = never.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 1000), new object[0]));
}
config.Save();
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to make config entries: " + ex));
}
finally
{
config.SaveOnConfigSet = saveOnConfigSet;
}
Plugin.Log.LogInfo((object)("Spawn weights for " + s_Entries.Count + " weapons in " + config.ConfigFilePath));
}
[HarmonyPostfix]
[HarmonyPriority(0)]
[HarmonyPatch(typeof(WeaponSelectionHandler), "GenerateWeaponRarityArray")]
private static void AfterGenerate(WeaponSelectionHandler __instance)
{
s_LastBuiltOnline = MatchmakingHandler.IsNetworkMatch;
if (s_LastBuiltOnline)
{
return;
}
try
{
List<byte> list = s_Bag.Invoke(__instance);
if (list == null)
{
return;
}
foreach (KeyValuePair<SingleWeaponUI, ConfigEntry<int>> s_Entry in s_Entries)
{
int value = s_Entry.Value.Value;
if (value < 0)
{
continue;
}
byte index = s_Entry.Key.WeaponIndex;
list.RemoveAll((byte b) => b == index);
if ((bool)s_IsActive.Invoke(__instance, new object[1] { s_Entry.Key }))
{
for (int num = 0; num < value; num++)
{
list.Add(index);
}
}
}
LogOdds(list);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to apply spawn weights: " + ex));
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(WeaponSelectionHandler), "GetRandomWeaponIndex")]
private static void BeforeDraw(WeaponSelectionHandler __instance)
{
if (s_LastBuiltOnline != MatchmakingHandler.IsNetworkMatch)
{
s_Generate.Invoke(__instance, null);
}
}
internal static void Rebuild()
{
if ((Object)(object)s_Handler == (Object)null)
{
return;
}
try
{
s_Generate.Invoke(s_Handler, null);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to rebuild the spawn bag: " + ex));
}
}
private static void LogOdds(List<byte> bag)
{
Dictionary<byte, int> dictionary = new Dictionary<byte, int>();
foreach (byte item in bag)
{
dictionary.TryGetValue(item, out var value);
dictionary[item] = value + 1;
}
StringBuilder stringBuilder = new StringBuilder("Spawn odds:");
if (bag.Count == 0)
{
stringBuilder.Append(" nothing can spawn");
}
foreach (SingleWeaponUI key in s_Entries.Keys)
{
if (dictionary.TryGetValue(key.WeaponIndex, out var value2))
{
stringBuilder.AppendFormat(" {0} {1:0.#}%,", key.WeaponName, 100f * (float)value2 / (float)bag.Count);
}
}
string text = stringBuilder.ToString().TrimEnd(new char[1] { ',' });
if (!(text == s_LastOdds))
{
s_LastOdds = text;
Plugin.Log.LogInfo((object)text);
}
}
private static string Clean(string s)
{
if (s == null)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
foreach (char value in s)
{
if ("=\n\t\r\\\"'[]".IndexOf(value) < 0)
{
stringBuilder.Append(value);
}
}
return stringBuilder.ToString().Trim();
}
}