using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("SlowHunger")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("SlowHunger")]
[assembly: AssemblyTitle("SlowHunger")]
[assembly: AssemblyVersion("1.1.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 SlowHunger
{
[BepInPlugin("com.sillas.muck.slowhunger", "SlowHunger", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
private sealed class Preset
{
public readonly float Multiplier;
public readonly string Label;
public Preset(float multiplier, string label)
{
Multiplier = multiplier;
Label = label;
}
}
public const string PluginGuid = "com.sillas.muck.slowhunger";
public const string PluginName = "SlowHunger";
public const string PluginVersion = "1.1.0";
private const string SettingsApiGuid = "Terrain.MuckSettings";
private const string BetterSettingsApiGuid = "muck.mrboxxy.bettersettingsapi";
private const string MuckSaveGameGuid = "MuckSaveGame.MichMcb";
private const string UiSettingName = "Hunger duration";
private static readonly Preset[] StandardPresets = new Preset[12]
{
new Preset(1f, "Vanilla — 1x"),
new Preset(0.8f, "1.25x — 20% less drain"),
new Preset(2f / 3f, "1.5x — 33% less drain"),
new Preset(0.6f, "1.67x — 40% less drain"),
new Preset(0.5f, "2x — 50% less drain"),
new Preset(0.4f, "2.5x — 60% less drain"),
new Preset(1f / 3f, "3x — 67% less drain"),
new Preset(0.25f, "4x — 75% less drain"),
new Preset(0.2f, "5x — 80% less drain"),
new Preset(0.1f, "10x — 90% less drain"),
new Preset(0.05f, "20x — 95% less drain"),
new Preset(0f, "Infinite — no hunger drain")
};
private static Plugin instance;
private ConfigEntry<float> drainMultiplier;
private Harmony harmony;
private Type playerStatusType;
private FieldInfo instanceField;
private PropertyInfo instanceProperty;
private FieldInfo hungerDrainRateField;
private object currentStatus;
private float originalDrainRate;
private float lastAppliedMultiplier = -999f;
private bool reflectionReady;
private bool reflectionErrorLogged;
private void Awake()
{
instance = this;
((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
drainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Hunger", "DrainMultiplier", 0.6f, "Multiplicador da velocidade natural de perda de fome. 1.00 = vanilla; 0.25 = 75% menos drain e duracao 4x; 0.00 = sem perda natural. Faixa aplicada: 0.00 a 2.00.");
((BaseUnityPlugin)this).Logger.LogInfo((object)"SlowHunger 1.1.0 carregado. Apenas o hunger drain sera alterado.");
TryResolvePlayerStatus();
TryInstallSettingsUi();
}
private void TryInstallSettingsUi()
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Expected O, but got Unknown
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Expected O, but got Unknown
Type type = AccessTools.TypeByName("MuckSettings.Settings");
if (type == null)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"SlowHunger UI indisponivel: SettingsApi nao instalado. O arquivo .cfg continua funcionando normalmente.");
return;
}
MethodInfo methodInfo = AccessTools.Method(type, "Gameplay", (Type[])null, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"SlowHunger UI indisponivel: MuckSettings.Settings.Gameplay nao foi localizado.");
return;
}
harmony = new Harmony("com.sillas.muck.slowhunger");
try
{
harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "GameplaySettingsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Type type2 = AccessTools.TypeByName("Settings");
MethodInfo methodInfo2 = ((type2 == null) ? null : AccessTools.Method(type2, "Start", (Type[])null, (Type[])null));
if (methodInfo2 != null)
{
HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "SettingsStartPostfix", (Type[])null);
val.priority = 0;
harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("SlowHunger UI indisponivel: falha ao aplicar o patch do SettingsApi. O .cfg continua ativo. " + ex));
harmony.UnpatchSelf();
harmony = null;
return;
}
((BaseUnityPlugin)this).Logger.LogInfo((object)"SlowHunger UI integrada a aba Gameplay do SettingsApi.");
}
private static void GameplaySettingsPostfix(object page)
{
if ((Object)(object)instance == (Object)null || page == null)
{
return;
}
try
{
instance.AddHungerSlider(page);
}
catch (Exception ex)
{
((BaseUnityPlugin)instance).Logger.LogError((object)("SlowHunger: falha ao criar o slider de fome. O .cfg continua ativo. " + ex));
}
}
private static void SettingsStartPostfix(object __instance)
{
if ((Object)(object)instance == (Object)null || __instance == null)
{
return;
}
try
{
instance.MoveAutoSaveNextToHunger(__instance);
}
catch (Exception ex)
{
((BaseUnityPlugin)instance).Logger.LogWarning((object)("SlowHunger UI: nao foi possivel reorganizar Auto Save. " + ex));
}
}
private void AddHungerSlider(object page)
{
Type type = AccessTools.TypeByName("MuckSettings.SliderSetting");
if (type == null)
{
throw new InvalidOperationException("MuckSettings.SliderSetting nao encontrado.");
}
MethodInfo methodInfo = FindAddSliderMethod(page.GetType());
if (methodInfo == null)
{
throw new MissingMethodException(page.GetType().FullName, "AddSliderSetting(string, int, int, int, Action<int>)");
}
List<Component> list = FindSliderComponents(page, type);
for (int i = 0; i < list.Count; i++)
{
if (ReadStringField(list[i], "settingName") == "Hunger duration")
{
return;
}
}
List<Preset> presets = BuildPresets(ClampMultiplier(drainMultiplier.Value));
int num = FindCurrentPresetIndex(presets, ClampMultiplier(drainMultiplier.Value));
object createdControl = null;
Action<int> action = delegate(int index)
{
if (index >= 0 && index < presets.Count)
{
drainMultiplier.Value = presets[index].Multiplier;
if (createdControl != null)
{
SetSliderText(createdControl, presets[index].Label);
}
((BaseUnityPlugin)this).Logger.LogInfo((object)("SlowHunger UI: selecionado " + presets[index].Label + "."));
}
};
HashSet<int> hashSet = new HashSet<int>();
for (int num2 = 0; num2 < list.Count; num2++)
{
hashSet.Add(((Object)list[num2]).GetInstanceID());
}
methodInfo.Invoke(page, new object[5]
{
"Hunger duration",
num,
0,
presets.Count - 1,
action
});
List<Component> list2 = FindSliderComponents(page, type);
for (int num3 = 0; num3 < list2.Count; num3++)
{
Component val = list2[num3];
if (!hashSet.Contains(((Object)val).GetInstanceID()) && ReadStringField(val, "settingName") == "Hunger duration")
{
createdControl = val;
break;
}
}
if (createdControl == null)
{
for (int num4 = list2.Count - 1; num4 >= 0; num4--)
{
if (ReadStringField(list2[num4], "settingName") == "Hunger duration")
{
createdControl = list2[num4];
break;
}
}
}
if (createdControl == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"SlowHunger: slider criado, mas o componente visual nao foi localizado; o valor numerico ainda funcionara.");
return;
}
SetBooleanProperty(ReadField(createdControl, "slider"), "wholeNumbers", value: true);
SetSliderText(createdControl, presets[num].Label);
((BaseUnityPlugin)this).Logger.LogInfo((object)("SlowHunger UI: slider criado com " + presets[num].Label + "."));
}
private void MoveAutoSaveNextToHunger(object gameSettings)
{
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
object obj = ReadField(gameSettings, "tutorial");
Component val = (Component)((obj is Component) ? obj : null);
if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent == (Object)null)
{
return;
}
Transform parent = val.transform.parent;
RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
RectTransform val3 = (RectTransform)(((Object)(object)val2 == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/);
if ((Object)(object)val3 == (Object)null)
{
return;
}
RectTransform val4 = FindDirectChildWithText(val2, "Auto Save");
RectTransform val5 = FindHungerRoot(val3);
if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null)
{
return;
}
((Transform)val4).SetParent((Transform)(object)val3, false);
((Transform)val4).localScale = Vector3.one;
((Transform)val4).SetSiblingIndex(((Transform)val5).GetSiblingIndex());
VerticalLayoutGroup component = ((Component)val3).GetComponent<VerticalLayoutGroup>();
if (!((Object)(object)component != (Object)null))
{
return;
}
float num = ((LayoutGroup)component).padding.top + ((LayoutGroup)component).padding.bottom;
int num2 = 0;
for (int i = 0; i < ((Transform)val3).childCount; i++)
{
Transform child = ((Transform)val3).GetChild(i);
RectTransform val6 = (RectTransform)(object)((child is RectTransform) ? child : null);
if (!((Object)(object)val6 == (Object)null) && ((Component)val6).gameObject.activeSelf && !IsIgnoredByLayout(val6))
{
float num3 = num;
Rect rect = val6.rect;
num = num3 + Math.Abs(((Rect)(ref rect)).height);
num2++;
}
}
if (num2 > 1)
{
num += ((HorizontalOrVerticalLayoutGroup)component).spacing * (float)(num2 - 1);
}
val3.SetSizeWithCurrentAnchors((Axis)1, num);
LayoutRebuilder.ForceRebuildLayoutImmediate(val3);
((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format(CultureInfo.InvariantCulture, "SlowHunger UI: Auto Save movido antes de Hunger duration; altura da lista = {0:0.##}.", new object[1] { num }));
}
private static RectTransform FindDirectChildWithText(RectTransform parent, string expectedText)
{
for (int i = 0; i < ((Transform)parent).childCount; i++)
{
Transform child = ((Transform)parent).GetChild(i);
RectTransform val = (RectTransform)(object)((child is RectTransform) ? child : null);
if ((Object)(object)val == (Object)null)
{
continue;
}
Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(true);
foreach (Component val2 in componentsInChildren)
{
PropertyInfo propertyInfo = AccessTools.Property(((object)val2).GetType(), "text");
if (!(propertyInfo == null) && !(propertyInfo.PropertyType != typeof(string)) && propertyInfo.CanRead && propertyInfo.GetValue(val2, null) as string == expectedText)
{
return val;
}
}
}
return null;
}
private static RectTransform FindHungerRoot(RectTransform content)
{
Type type = AccessTools.TypeByName("MuckSettings.SliderSetting");
if (type == null)
{
return null;
}
Component[] componentsInChildren = ((Component)content).GetComponentsInChildren(type, true);
foreach (Component val in componentsInChildren)
{
if (!(ReadStringField(val, "settingName") != "Hunger duration"))
{
Transform val2 = val.transform;
while ((Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)content)
{
val2 = val2.parent;
}
if (!((Object)(object)val2.parent == (Object)(object)content))
{
return null;
}
return (RectTransform)(object)((val2 is RectTransform) ? val2 : null);
}
}
return null;
}
private static bool IsIgnoredByLayout(RectTransform child)
{
Component[] components = ((Component)child).GetComponents<Component>();
foreach (Component obj in components)
{
ILayoutIgnorer val = (ILayoutIgnorer)(object)((obj is ILayoutIgnorer) ? obj : null);
if (val != null && val.ignoreLayout)
{
return true;
}
}
return false;
}
private static MethodInfo FindAddSliderMethod(Type pageType)
{
MethodInfo[] methods = pageType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
foreach (MethodInfo methodInfo in methods)
{
if (!(methodInfo.Name != "AddSliderSetting"))
{
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length == 5 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int) && parameters[3].ParameterType == typeof(int) && parameters[4].ParameterType == typeof(Action<int>))
{
return methodInfo;
}
}
}
return null;
}
private static List<Component> FindSliderComponents(object page, Type sliderSettingType)
{
List<Component> list = new List<Component>();
FieldInfo fieldInfo = AccessTools.Field(page.GetType(), "content");
Component val = (Component)((fieldInfo == null) ? null : /*isinst with value type is only supported in some contexts*/);
if ((Object)(object)val != (Object)null)
{
Component[] componentsInChildren = val.GetComponentsInChildren(sliderSettingType, true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
list.Add(componentsInChildren[i]);
}
return list;
}
Object[] array = Resources.FindObjectsOfTypeAll(sliderSettingType);
foreach (Object obj in array)
{
Component val2 = (Component)(object)((obj is Component) ? obj : null);
if ((Object)(object)val2 != (Object)null)
{
list.Add(val2);
}
}
return list;
}
private static List<Preset> BuildPresets(float currentMultiplier)
{
List<Preset> list = new List<Preset>(StandardPresets);
for (int i = 0; i < list.Count; i++)
{
if (NearlyEqual(list[i].Multiplier, currentMultiplier))
{
return list;
}
}
Preset item = new Preset(currentMultiplier, BuildCustomLabel(currentMultiplier));
float num = DurationFor(currentMultiplier);
int index = list.Count - 1;
for (int j = 0; j < list.Count - 1; j++)
{
if (DurationFor(list[j].Multiplier) > num)
{
index = j;
break;
}
}
list.Insert(index, item);
return list;
}
private static int FindCurrentPresetIndex(List<Preset> presets, float multiplier)
{
for (int i = 0; i < presets.Count; i++)
{
if (NearlyEqual(presets[i].Multiplier, multiplier))
{
return i;
}
}
return 0;
}
private static string BuildCustomLabel(float multiplier)
{
if (multiplier <= 0f)
{
return "Infinite — no hunger drain";
}
float num = 1f / multiplier;
if (multiplier <= 1f)
{
int num2 = (int)Math.Round((1f - multiplier) * 100f);
return string.Format(CultureInfo.InvariantCulture, "Custom — {0}x / {1}% less drain", new object[2]
{
num.ToString("0.##", CultureInfo.InvariantCulture),
num2
});
}
int num3 = (int)Math.Round((multiplier - 1f) * 100f);
return string.Format(CultureInfo.InvariantCulture, "Custom — {0}x / {1}% more drain", new object[2]
{
num.ToString("0.##", CultureInfo.InvariantCulture),
num3
});
}
private static float DurationFor(float multiplier)
{
if (!(multiplier <= 0f))
{
return 1f / multiplier;
}
return float.PositiveInfinity;
}
private static bool NearlyEqual(float first, float second)
{
return Math.Abs(first - second) < 0.0005f;
}
private static object ReadField(object target, string fieldName)
{
if (target == null)
{
return null;
}
FieldInfo fieldInfo = AccessTools.Field(target.GetType(), fieldName);
if (!(fieldInfo == null))
{
return fieldInfo.GetValue(target);
}
return null;
}
private static string ReadStringField(object target, string fieldName)
{
return ReadField(target, fieldName)?.ToString();
}
private static void SetSliderText(object sliderControl, string text)
{
object obj = ReadField(sliderControl, "value");
if (obj != null)
{
SetProperty(obj, "text", text);
SetProperty(obj, "enableAutoSizing", true);
SetProperty(obj, "fontSizeMin", 10f);
SetProperty(obj, "fontSizeMax", 22f);
}
}
private static void SetBooleanProperty(object target, string propertyName, bool value)
{
SetProperty(target, propertyName, value);
}
private static void SetProperty(object target, string propertyName, object value)
{
if (target != null)
{
PropertyInfo propertyInfo = AccessTools.Property(target.GetType(), propertyName);
if (propertyInfo != null && propertyInfo.CanWrite)
{
propertyInfo.SetValue(target, value, null);
}
}
}
private void TryResolvePlayerStatus()
{
if (reflectionReady)
{
return;
}
playerStatusType = Type.GetType("PlayerStatus, Assembly-CSharp", throwOnError: false);
if (playerStatusType == null)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
if (assemblies[i].GetName().Name == "Assembly-CSharp")
{
playerStatusType = assemblies[i].GetType("PlayerStatus", throwOnError: false);
if (playerStatusType != null)
{
break;
}
}
}
}
if (playerStatusType == null)
{
return;
}
BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
BindingFlags bindingAttr2 = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
instanceField = playerStatusType.GetField("Instance", bindingAttr);
instanceProperty = playerStatusType.GetProperty("Instance", bindingAttr);
hungerDrainRateField = playerStatusType.GetField("hungerDrainRate", bindingAttr2);
if ((instanceField == null && instanceProperty == null) || hungerDrainRateField == null)
{
if (!reflectionErrorLogged)
{
((BaseUnityPlugin)this).Logger.LogError((object)"SlowHunger: nao encontrei PlayerStatus.Instance ou hungerDrainRate. Nenhuma alteracao foi aplicada.");
reflectionErrorLogged = true;
}
}
else
{
reflectionReady = true;
((BaseUnityPlugin)this).Logger.LogInfo((object)"SlowHunger: PlayerStatus.hungerDrainRate encontrado.");
}
}
private object GetPlayerStatusInstance()
{
if (instanceField != null)
{
return instanceField.GetValue(null);
}
if (instanceProperty != null)
{
return instanceProperty.GetValue(null, null);
}
return null;
}
private void Update()
{
if (!reflectionReady)
{
TryResolvePlayerStatus();
return;
}
object playerStatusInstance = GetPlayerStatusInstance();
if (playerStatusInstance == null)
{
currentStatus = null;
lastAppliedMultiplier = -999f;
return;
}
if (currentStatus != playerStatusInstance)
{
currentStatus = playerStatusInstance;
object value = hungerDrainRateField.GetValue(playerStatusInstance);
originalDrainRate = Convert.ToSingle(value);
lastAppliedMultiplier = -999f;
((BaseUnityPlugin)this).Logger.LogInfo((object)("SlowHunger: hungerDrainRate base detectado = " + originalDrainRate.ToString("0.###", CultureInfo.InvariantCulture)));
}
float num = ClampMultiplier(drainMultiplier.Value);
if (!(Math.Abs(num - lastAppliedMultiplier) < 0.0001f))
{
float num2 = originalDrainRate * num;
hungerDrainRateField.SetValue(playerStatusInstance, num2);
lastAppliedMultiplier = num;
((BaseUnityPlugin)this).Logger.LogInfo((object)("SlowHunger: hunger drain ajustado " + originalDrainRate.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + num2.ToString("0.###", CultureInfo.InvariantCulture) + " (" + num.ToString("0.##", CultureInfo.InvariantCulture) + "x)"));
}
}
private static float ClampMultiplier(float multiplier)
{
if (multiplier < 0f)
{
return 0f;
}
if (multiplier > 2f)
{
return 2f;
}
return multiplier;
}
private void OnDestroy()
{
if (harmony != null)
{
harmony.UnpatchSelf();
}
if (reflectionReady && currentStatus != null && hungerDrainRateField != null)
{
try
{
hungerDrainRateField.SetValue(currentStatus, originalDrainRate);
}
catch
{
}
}
if (instance == this)
{
instance = null;
}
}
}
}