using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Joive")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Keeps level retry progress and supplies after death/loss in Burglin Gnomes.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("BG NoDeathPenalty")]
[assembly: AssemblyTitle("BG-NoDeathPenalty-Joive")]
[assembly: AssemblyVersion("1.0.1.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 BGNoDeathPenalty
{
[BepInPlugin("joive.bg.nodeathpenalty", "BG NoDeathPenalty", "1.0.1")]
public sealed class NoDeathPenaltyPlugin : BaseUnityPlugin
{
private static NoDeathPenaltyPlugin Instance;
private Harmony harmony;
private static ConfigEntry<bool> keepPlayerInventory;
private static ConfigEntry<bool> keepStockpileSupplies;
private static ConfigEntry<bool> keepLevelProgression;
private static ConfigEntry<bool> retrySameDayOnLoss;
private static ConfigEntry<bool> debugLogs;
private static List<int> preRunPlayedHistory;
private static List<int> preRunShownHistory;
private static int levelBeforeRun = -1;
private static bool runStarted;
private static bool runWon;
private static bool resetGameInProgress;
private static bool skipResetSaveOnce;
private void Awake()
{
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Expected O, but got Unknown
Instance = this;
keepPlayerInventory = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepPlayerInventory", true, "Prevent player inventory from dropping/clearing after death or end-of-run respawn.");
keepStockpileSupplies = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepStockpileSupplies", true, "Prevent the home stockpile/resource box from being cleared on loss/reset.");
keepLevelProgression = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepLevelProgression", true, "Try to keep level progression/history when a loss resets the day.");
retrySameDayOnLoss = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RetrySameDayOnLoss", true, "After an unsuccessful run, restore the pre-run level selection so the same day can be replayed.");
debugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogs", true, "Write detailed patch activity to BepInEx LogOutput.log.");
harmony = new Harmony("joive.bg.nodeathpenalty");
PatchSafe(typeof(InventoryBase), "DropInventoryContents", typeof(NoDeathPenaltyPlugin), "InventoryDropPrefix");
PatchSafe(typeof(InventoryBase), "ClearInventory", typeof(NoDeathPenaltyPlugin), "InventoryClearPrefix");
PatchSafe(typeof(LevelProgressionHandler), "OnConfirmLevel", typeof(NoDeathPenaltyPlugin), "OnConfirmLevelPrefix");
PatchSafe(typeof(GameProgressionManager), "OnGameStarted", typeof(NoDeathPenaltyPlugin), "OnGameStartedPrefix");
PatchSafe(typeof(GameProgressionManager), "OnGameWon", typeof(NoDeathPenaltyPlugin), "OnGameWonPostfix", postfix: true);
PatchSafe(typeof(GameProgressionManager), "OnGameEnded", typeof(NoDeathPenaltyPlugin), "OnGameEndedPostfix", postfix: true);
PatchSafe(typeof(GameProgressionManager), "ResetGame", typeof(NoDeathPenaltyPlugin), "ResetGamePrefix");
PatchSafe(typeof(GameProgressionManager), "ResetGame", typeof(NoDeathPenaltyPlugin), "ResetGamePostfix", postfix: true);
PatchSafe(typeof(GameProgressionManager), "SaveGame", typeof(NoDeathPenaltyPlugin), "SaveGamePrefix");
Log("BG NoDeathPenalty 1.0.1 loaded. Home reset is no longer blocked; this prevents missing-house softlocks.");
}
private void OnDestroy()
{
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
}
private void PatchSafe(Type targetType, string targetMethod, Type patchType, string patchMethod, bool postfix = false)
{
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Expected O, but got Unknown
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Expected O, but got Unknown
try
{
MethodInfo methodInfo = AccessTools.Method(targetType, targetMethod, (Type[])null, (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethod, (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Missing target method: " + targetType.Name + "." + targetMethod));
return;
}
if (methodInfo2 == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Missing patch method: " + patchType.Name + "." + patchMethod));
return;
}
if (postfix)
{
harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
else
{
harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
Log("Patched " + targetType.Name + "." + targetMethod + " -> " + patchMethod);
}
catch (Exception arg)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to patch {targetType.Name}.{targetMethod}: {arg}");
}
}
public static bool InventoryDropPrefix(InventoryBase __instance)
{
if ((Object)(object)__instance == (Object)null)
{
return true;
}
if (keepPlayerInventory.Value && IsPlayerInventory(__instance))
{
Log("Blocked player inventory drop.");
return false;
}
return true;
}
public static bool InventoryClearPrefix(InventoryBase __instance)
{
if ((Object)(object)__instance == (Object)null)
{
return true;
}
if (keepPlayerInventory.Value && IsPlayerInventory(__instance))
{
Log("Blocked player inventory clear.");
return false;
}
if (keepStockpileSupplies.Value && IsStockpileInventory(__instance))
{
Log("Blocked stockpile/resource box clear.");
return false;
}
return true;
}
public static void OnConfirmLevelPrefix(LevelProgressionHandler __instance)
{
CaptureLevelState(__instance, "level confirmed");
}
public static void OnGameStartedPrefix(GameProgressionManager __instance)
{
if (!((Object)(object)__instance == (Object)null))
{
levelBeforeRun = __instance.CurrentLevel;
runStarted = true;
runWon = false;
CaptureLevelState(__instance.LevelProgressionHandler, "game started");
Log($"Game started. Level before run={levelBeforeRun}");
}
}
public static void OnGameWonPostfix(GameProgressionManager __instance)
{
runWon = true;
preRunPlayedHistory = null;
preRunShownHistory = null;
levelBeforeRun = -1;
runStarted = false;
resetGameInProgress = false;
skipResetSaveOnce = false;
Log("Game won. Progression snapshot cleared.");
}
public unsafe static void OnGameEndedPostfix(GameProgressionManager __instance, GameEndedReason reason)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Invalid comparison between Unknown and I4
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Invalid comparison between Unknown and I4
if (!((Object)(object)__instance == (Object)null) && keepLevelProgression.Value && !runWon && ((int)reason == 1 || (int)reason == 2))
{
Log("Loss/end detected: " + ((object)(*(GameEndedReason*)(&reason))/*cast due to .constrained prefix*/).ToString() + ". Waiting for normal reset, then restoring level state.");
}
}
public static bool ResetGamePrefix(GameProgressionManager __instance)
{
if ((Object)(object)__instance == (Object)null || !keepLevelProgression.Value || !runStarted || runWon)
{
return true;
}
resetGameInProgress = true;
skipResetSaveOnce = true;
if (levelBeforeRun < 0)
{
levelBeforeRun = Math.Max(0, __instance.CurrentLevel - 1);
}
CaptureLevelState(__instance.LevelProgressionHandler, "before ResetGame");
Log("ResetGame will run normally so the house/UI respawn. SaveGame inside ResetGame will be skipped once, then level state restored.");
return true;
}
public static void ResetGamePostfix(GameProgressionManager __instance)
{
if ((Object)(object)__instance == (Object)null || !resetGameInProgress)
{
return;
}
try
{
RestoreRetryState(__instance, "ResetGame postfix");
}
finally
{
resetGameInProgress = false;
skipResetSaveOnce = false;
}
}
public static bool SaveGamePrefix(GameProgressionManager __instance)
{
if (skipResetSaveOnce && keepLevelProgression.Value)
{
skipResetSaveOnce = false;
Log("Skipped SaveGame inside ResetGame to avoid deleting/reverting progression while retrying the same day.");
return false;
}
return true;
}
private static void CaptureLevelState(LevelProgressionHandler handler, string reason)
{
if (keepLevelProgression.Value && retrySameDayOnLoss.Value && handler != null)
{
preRunPlayedHistory = CopyList(handler.PlayedLevelHistory);
preRunShownHistory = CopyList(handler.ShowedLevelHistory);
Log($"Captured level history ({reason}): played={Count(preRunPlayedHistory)}, shown={Count(preRunShownHistory)}");
}
}
private static void RestoreRetryState(GameProgressionManager progression, string reason)
{
try
{
if (!((Object)(object)progression == (Object)null))
{
int num = ((levelBeforeRun >= 0) ? levelBeforeRun : Math.Max(0, progression.CurrentLevel));
SetNetworkVariableValue(progression, "n_level", num);
SetNetworkVariableValue(progression, "n_day", 1);
if (retrySameDayOnLoss.Value && preRunPlayedHistory != null && preRunShownHistory != null)
{
progression.LevelProgressionHandler.SetFromSavedState(CopyList(preRunPlayedHistory), CopyList(preRunShownHistory));
Log($"Restored pre-run level options/history for retry. played={Count(preRunPlayedHistory)}, shown={Count(preRunShownHistory)}");
}
progression.SelectedLevelIndex = 0;
runStarted = false;
runWon = false;
Log($"Restored retry state after {reason}. CurrentLevel set to {num}.");
}
}
catch (Exception ex)
{
NoDeathPenaltyPlugin instance = Instance;
if (instance != null)
{
((BaseUnityPlugin)instance).Logger.LogWarning((object)("Failed to restore retry state: " + ex));
}
}
}
private static void SetNetworkVariableValue(object owner, string fieldName, int value)
{
object obj = AccessTools.Field(owner.GetType(), fieldName)?.GetValue(owner);
(obj?.GetType().GetProperty("Value"))?.SetValue(obj, value, null);
}
private static bool IsPlayerInventory(InventoryBase inventory)
{
return inventory is PlayerInventory;
}
private static bool IsStockpileInventory(InventoryBase inventory)
{
try
{
GameProgressionManager instance = GameProgressionManager.Instance;
return (Object)(object)instance != (Object)null && (Object)(object)instance.Deposit != (Object)null && instance.Deposit.ResourceInventory == inventory;
}
catch
{
return false;
}
}
private static List<int> CopyList(List<int> source)
{
if (source != null)
{
return new List<int>(source);
}
return null;
}
private static int Count(List<int> list)
{
return list?.Count ?? 0;
}
private static void Log(string message)
{
if (debugLogs != null && debugLogs.Value)
{
NoDeathPenaltyPlugin instance = Instance;
if (instance != null)
{
((BaseUnityPlugin)instance).Logger.LogInfo((object)message);
}
}
}
}
}