using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using YAPYAP;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("QuotaDifficulty")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+cd7c6cb439467ad6465739ddd2d2e163716004eb")]
[assembly: AssemblyProduct("QuotaDifficulty")]
[assembly: AssemblyTitle("QuotaDifficulty")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace QuotaDifficulty
{
public enum DifficultyPreset
{
Easiest,
Easier,
Easy,
Original,
Hard,
Harder,
Hardest,
Custom
}
public static class DifficultyState
{
private static bool _frozen;
private static float _frozenMultiplier = 1f;
public static bool IsFrozen => _frozen;
public static void Freeze(float multiplier)
{
_frozen = true;
_frozenMultiplier = multiplier;
}
public static void Clear()
{
_frozen = false;
_frozenMultiplier = 1f;
}
public static float Resolve(float liveMultiplier)
{
if (!_frozen)
{
return liveMultiplier;
}
return _frozenMultiplier;
}
}
internal sealed class ModConfig
{
private readonly ConfigEntry<DifficultyPreset> _preset;
private readonly ConfigEntry<float> _customMultiplier;
private readonly ConfigEntry<bool> _verboseLogging;
public DifficultyPreset Preset => _preset.Value;
public float CustomMultiplier => _customMultiplier.Value;
public bool VerboseLogging => _verboseLogging.Value;
public float Multiplier => QuotaMath.MultiplierFor(Preset, CustomMultiplier);
public ModConfig(ConfigFile file)
{
_preset = file.Bind<DifficultyPreset>("Difficulty", "Preset", DifficultyPreset.Original, "Quota goal difficulty. Easiest 50%, Easier 65%, Easy 80%, Original 100%, Hard 125%, Harder 150%, Hardest 200%. Choose Custom to use CustomMultiplier instead.");
_customMultiplier = file.Bind<float>("Difficulty", "CustomMultiplier", 1f, "Multiplier used when Preset is Custom. Clamped to the range 0.05 - 10.0.");
_verboseLogging = file.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log every quota goal calculation. Use this when reporting a problem.");
}
}
[BepInPlugin("psh_games.quotadifficulty", "QuotaDifficulty", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "psh_games.quotadifficulty";
public const string PluginName = "QuotaDifficulty";
public const string PluginVersion = "1.0.0";
internal static ManualLogSource Log { get; private set; }
internal static ModConfig Settings { get; private set; }
internal static float ActiveMultiplier => DifficultyState.Resolve((Settings == null) ? 1f : Settings.Multiplier);
private void Awake()
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Settings = new ModConfig(((BaseUnityPlugin)this).Config);
try
{
new Harmony("psh_games.quotadifficulty").PatchAll(typeof(Plugin).Assembly);
}
catch (Exception ex)
{
Log.LogError((object)("Failed to apply patches; the game runs unmodified. " + ex));
return;
}
Log.LogInfo((object)string.Format("{0} {1} loaded. preset={2} multiplier={3:0.###}", "QuotaDifficulty", "1.0.0", Settings.Preset, Settings.Multiplier));
}
}
public static class QuotaMath
{
public const float MinMultiplier = 0.05f;
public const float MaxMultiplier = 10f;
public const int MinGoal = 1;
public static float MultiplierFor(DifficultyPreset preset, float customMultiplier)
{
return preset switch
{
DifficultyPreset.Easiest => 0.5f,
DifficultyPreset.Easier => 0.65f,
DifficultyPreset.Easy => 0.8f,
DifficultyPreset.Original => 1f,
DifficultyPreset.Hard => 1.25f,
DifficultyPreset.Harder => 1.5f,
DifficultyPreset.Hardest => 2f,
DifficultyPreset.Custom => Clamp(customMultiplier),
_ => 1f,
};
}
public static float Clamp(float multiplier)
{
if (float.IsNaN(multiplier))
{
return 1f;
}
if (multiplier < 0.05f)
{
return 0.05f;
}
if (multiplier > 10f)
{
return 10f;
}
return multiplier;
}
public static bool IsNeutral(float multiplier)
{
return multiplier == 1f;
}
public static int Scale(int baseGoal, float multiplier)
{
if (IsNeutral(multiplier))
{
return baseGoal;
}
double num = Math.Round((double)baseGoal * (double)multiplier, MidpointRounding.AwayFromZero);
if (num < 1.0)
{
return 1;
}
if (num > 2147483647.0)
{
return int.MaxValue;
}
return (int)num;
}
}
}
namespace QuotaDifficulty.Patches
{
[HarmonyPatch(typeof(GameManager))]
internal static class GameEntryPatches
{
[HarmonyPrefix]
[HarmonyPatch("OnStartServer")]
private static void FreezeMultiplier(GameManager __instance)
{
float multiplier = Plugin.Settings.Multiplier;
DifficultyState.Freeze(multiplier);
Plugin.Log.LogInfo((object)$"Game entered. multiplier frozen at {multiplier:0.###} (preset {Plugin.Settings.Preset}).");
LogBaseValues(__instance);
}
[HarmonyPostfix]
[HarmonyPatch("OnDestroy")]
private static void ClearMultiplier()
{
if (DifficultyState.IsFrozen)
{
DifficultyState.Clear();
Plugin.Log.LogInfo((object)"Game left. multiplier unfrozen.");
}
}
private static void LogBaseValues(GameManager instance)
{
if (Plugin.Settings.VerboseLogging)
{
int[] array = ReadField<int[]>(instance, "manualQuotaGoals");
string text = ((array != null) ? string.Join(", ", array) : "unavailable");
Plugin.Log.LogInfo((object)string.Format("Game base values: manualQuotaGoals=[{0}] sessionQuotaMultiplier={1} quotaScoreGoal={2} roundsToQuota={3}", text, ReadField<float>(instance, "sessionQuotaMultiplier"), ReadField<int>(instance, "quotaScoreGoal"), ReadField<int>(instance, "roundsToQuota")));
}
}
private static T ReadField<T>(GameManager instance, string name)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(GameManager), name);
if (fieldInfo == null)
{
return default(T);
}
object value = fieldInfo.GetValue(instance);
if (value is T)
{
return (T)value;
}
return default(T);
}
}
[HarmonyPatch(typeof(GameManager))]
internal static class QuotaScalingPatches
{
[HarmonyPostfix]
[HarmonyPatch("GetQuotaForSession")]
private static void ScaleSessionGoal(ref int __result)
{
int num = QuotaMath.Scale(__result, Plugin.ActiveMultiplier);
if (num != __result && Plugin.Settings.VerboseLogging)
{
Plugin.Log.LogInfo((object)$"GetQuotaForSession {__result} -> {num}");
}
__result = num;
}
[HarmonyPostfix]
[HarmonyPatch("SvResetGameState")]
private static void RewriteGoalAfterReset(GameManager __instance)
{
ApplyGoal(__instance, "SvResetGameState");
}
[HarmonyPostfix]
[HarmonyPatch("RestoreGameData")]
private static void RewriteGoalAfterRestore(GameManager __instance)
{
ApplyGoal(__instance, "RestoreGameData");
}
[HarmonyPostfix]
[HarmonyPatch("GetNextQuotaScoreGoal")]
private static void RewriteNextGoal(GameManager __instance, ref int __result)
{
if (!QuotaMath.IsNeutral(Plugin.ActiveMultiplier))
{
__result = __instance.GetQuotaForSession(__instance.QuotaSessionsCompleted + 1);
}
}
private static void ApplyGoal(GameManager instance, string source)
{
if (!QuotaMath.IsNeutral(Plugin.ActiveMultiplier) && NetworkServer.active)
{
int num = (instance.NetworkcurrentQuotaScoreGoal = instance.GetQuotaForSession(instance.QuotaSessionsCompleted));
if (Plugin.Settings.VerboseLogging)
{
Plugin.Log.LogInfo((object)$"{source}: sessionsCompleted={instance.QuotaSessionsCompleted} goal={num}");
}
}
}
}
}