Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Schedule 1 Survival Mode v0.6.0
S1HungerThirst.dll
Decompiled 4 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.AvatarFramework; using Il2CppScheduleOne.AvatarFramework.Emotions; using Il2CppScheduleOne.Core.Items.Framework; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Dialogue; using Il2CppScheduleOne.Effects; using Il2CppScheduleOne.Equipping; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.PlayerScripts.Health; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.Storage; using Il2CppScheduleOne.Trash; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Shop; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using S1HungerThirst; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "Schedule 1 Survival Mode", "0.6.0", "Schedule 1 Survival Mode", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: VerifyLoaderVersion("0.7.0", true)] [assembly: HarmonyDontPatchAll] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Schedule 1 Survival Mode")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.0.0")] [assembly: AssemblyInformationalVersion("0.6.0")] [assembly: AssemblyProduct("Schedule 1 Survival Mode")] [assembly: AssemblyTitle("Schedule 1 Survival Mode")] [assembly: AssemblyVersion("0.6.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 S1HungerThirst { public readonly struct Restore { public readonly float Hunger; public readonly float Thirst; public readonly float Energy; public Restore(float hunger, float thirst, float energy = 0f) { Hunger = hunger; Thirst = thirst; Energy = energy; } } public static class ConsumableMap { private static readonly string[] FoodWords = new string[42] { "banana", "donut", "doughnut", "apple", "orange", "grape", "berry", "fruit", "chip", "crisp", "chocolate", "candy", "cookie", "biscuit", "snack", "sandwich", "burger", "pizza", "taco", "noodle", "soup", "meal", "bread", "cake", "pie", "granola", "cereal", "pretzel", "jerky", "hotdog", "muffin", "pastr", "bean", "chili", "chile", "pepper", "corn", "rice", "egg", "cheese", "tomato", "potato" }; private static readonly Dictionary<string, Restore> _map = new Dictionary<string, Restore>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _validatedMissing = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _registryScanned; private static readonly HashSet<string> _runtimeIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _foodMadeDrinkable; private static readonly HashSet<string> _wiredIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Random _toastRng = new Random(); private static readonly string[] EatLines = new string[14] { "Not bad, actually.", "That {0} didn't last long.", "Demolished the {0}.", "Yeah, that'll do for now.", "Could eat more, honestly.", "Health food, probably.", "Beats skipping lunch.", "Down the hatch.", "Better than nothing.", "Ate the {0}. No regrets yet.", "Something in the tank now.", "Could've used salt, but fine.", "The {0} went down easy.", "Won't win awards. It went down." }; private static readonly string[] DrinkLines = new string[11] { "Knocked back the {0}.", "Better. Still thirsty though.", "Could go another, honestly.", "Hydration, technically.", "Cheers, no one.", "Down in one.", "That's the edge off.", "Wet, at least.", "The {0} barely touched the sides.", "Good enough for now.", "Throat's happier." }; private static readonly string[] EnergyLines = new string[7] { "Bit more awake now.", "{0} kicked in.", "That's the motor turning over.", "Eyes open a bit wider now.", "That'll buy an hour or two.", "Less zombie now.", "Right. Back online, sort of." }; private static readonly string[] TacoLines = new string[9] { "Taco. Don't ask what's in it.", "Not bad for a servo taco.", "Drips everywhere. Ah well.", "Half of it went down my shirt.", "Structural failure halfway through. Worth it.", "The shell gave up before I did.", "Servo tacos. Reliable, in their own way.", "That one had some heat to it.", "Napkin situation: dire." }; private static readonly string[] ManukeLines = new string[7] { "That was four hundred and twenty dollars.", "Tastes expensive. Mostly.", "Pretty convincing wagyu, honestly.", "Four twenty well spent. Probably.", "Tastes like money leaving.", "Can confirm: expensive.", "Genuinely can't fault the fake wagyu." }; private static readonly string[] DanChiliLines = new string[4] { "Dan wasn't kidding about the kick.", "Hardware-store chili. Better than it has any right to be.", "The cumin's doing something in there. Something good.", "Eyes watering a little. Worth it." }; private static readonly string[] DanLasagnaLines = new string[4] { "The comedian's cut. Dan can cook.", "Hardware-store lasagna, and it's genuinely good. Weird day.", "That corner piece was earned.", "Dan builds a solid lasagna. Structurally sound, even." }; private static readonly string[] KyleSpecialLines = new string[7] { "That's a lot of...sauce", "There's so much sauce.", "Couldn't tell you what's under the sauce.", "The sauce outweighed the taco.", "Still finding sauce.", "White sauce all the way down.", "Asked no questions. Kept eating." }; public static void ParseConfigMap() { _map.Clear(); _registryScanned = false; _foodMadeDrinkable = false; string[] array = (NeedsConfig.FoodMapRaw ?? "").Split(';', StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('=', 2); if (array2.Length == 2) { string[] array3 = array2[1].Split('/', 3); float hunger = NeedsConfig.ParseF(array3[0].Trim(), 0f); float thirst = ((array3.Length > 1) ? NeedsConfig.ParseF(array3[1].Trim(), 0f) : 0f); float energy = ((array3.Length > 2) ? NeedsConfig.ParseF(array3[2].Trim(), 0f) : 0f); _map[array2[0].Trim()] = new Restore(hunger, thirst, energy); } } if (!NeedsConfig.GasolineDrinkable) { _map.Remove("gasoline"); } MelonLogger.Msg($"[Food] Config map: {_map.Count} item(s)."); } public static void ScanRegistry() { //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Invalid comparison between Unknown and I4 //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Invalid comparison between Unknown and I4 if (_registryScanned) { return; } _registryScanned = true; try { foreach (string key in _map.Keys) { if (!Registry.ItemExists(key) && _validatedMissing.Add(key)) { MelonLogger.Warning("[Food] Configured item id '" + key + "' does not exist in this game build; entry ignored."); } } if (!NeedsConfig.AutoMapUnknownConsumables) { return; } Il2CppArrayBase<ItemDefinition> val = (((Object)(object)Singleton<Registry>.Instance != (Object)null) ? Singleton<Registry>.Instance.GetAllItems() : null)?.ToArray(); if (val == null) { return; } int num = 0; for (int i = 0; i < val.Length; i++) { ItemDefinition val2 = val[i]; if ((Object)(object)val2 == (Object)null || string.IsNullOrEmpty(((BaseItemDefinition)val2).ID) || _map.ContainsKey(((BaseItemDefinition)val2).ID) || ((int)((BaseItemDefinition)val2).Category != 7 && (int)((BaseItemDefinition)val2).Category != 9) || !LooksLikeFood(((BaseItemDefinition)val2).ID, ((BaseItemDefinition)val2).Name)) { continue; } string text = "none"; try { if ((Object)(object)val2.Equippable != (Object)null) { text = ((MemberInfo)((Object)val2.Equippable).GetIl2CppType()).Name; } } catch { text = "?"; } if (text != "none" && text != "Equippable_Viewmodel" && text != "Equippable_Cuke") { VLog($"'{((BaseItemDefinition)val2).ID}' food-named but equip={text} — left alone"); } else { _map[((BaseItemDefinition)val2).ID] = new Restore(20f, 3f); num++; MelonLogger.Msg($"[Food] Auto-mapped food ingredient '{((BaseItemDefinition)val2).ID}' ({((BaseItemDefinition)val2).Name})."); } } if (num > 0) { MelonLogger.Msg($"[Food] Auto-mapped {num} additional edible(s) from the registry."); } if (!NeedsConfig.VerboseConsume) { return; } MelonLogger.Msg($"[Food] ===== FULL ITEM REGISTRY ({val.Length} defs) ====="); for (int j = 0; j < val.Length; j++) { ItemDefinition val3 = val[j]; if ((Object)(object)val3 == (Object)null || string.IsNullOrEmpty(((BaseItemDefinition)val3).ID)) { continue; } string value = "none"; try { if ((Object)(object)val3.Equippable != (Object)null) { value = ((MemberInfo)((Object)val3.Equippable).GetIl2CppType()).Name; } } catch { value = "?"; } bool flag = _map.ContainsKey(((BaseItemDefinition)val3).ID); MelonLogger.Msg($"[Food] {((BaseItemDefinition)val3).ID} | '{((BaseItemDefinition)val3).Name}' | cat={((BaseItemDefinition)val3).Category} | equip={value} | edible={(flag ? "YES" : "no")}"); } MelonLogger.Msg("[Food] ===== END REGISTRY DUMP ====="); } catch (Exception ex) { MelonLogger.Warning("[Food] Registry scan failed (will retry next session): " + ex.Message); _registryScanned = false; } } private static bool LooksLikeFood(string id, string name) { string text = (id + " " + name).ToLowerInvariant(); string[] foodWords = FoodWords; foreach (string value in foodWords) { if (text.Contains(value)) { return true; } } return false; } public static bool TryGet(string itemId, out Restore r) { return _map.TryGetValue(itemId ?? "", out r); } public static void AddRuntimeEntry(string id, Restore r) { if (!string.IsNullOrEmpty(id) && !_map.ContainsKey(id)) { _map[id] = r; _runtimeIds.Add(id); } } public static void OnSessionEnded() { _registryScanned = false; foreach (string runtimeId in _runtimeIds) { _map.Remove(runtimeId); } _runtimeIds.Clear(); } public static bool IsFood(Restore r) { return r.Hunger > r.Thirst; } public static bool WasWired(string id) { return _wiredIds.Contains(id ?? ""); } public static IEnumerable<string> WiredIds() { return _wiredIds; } public static void MakeFoodDrinkable() { //IL_01a4: Unknown result type (might be due to invalid IL or missing references) if (_foodMadeDrinkable) { return; } try { ItemDefinition item = Registry.GetItem("cuke"); if ((Object)(object)item == (Object)null || (Object)(object)item.Equippable == (Object)null) { MelonLogger.Warning("[Eat] cuke definition/equippable not found — cannot wire food consumption yet."); return; } if ((Object)(object)((Il2CppObjectBase)item.Equippable).TryCast<Equippable_Cuke>() == (Object)null) { MelonLogger.Warning("[Eat] cuke equippable is not Equippable_Cuke — wiring anyway."); } _foodMadeDrinkable = true; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (string key in _map.Keys) { ItemDefinition item2 = Registry.GetItem(key); if ((Object)(object)item2 == (Object)null) { num3++; MelonLogger.Msg("[Eat] '" + key + "' NOT IN REGISTRY — cannot wire."); continue; } if ((Object)(object)item2.Equippable != (Object)null && (Object)(object)((Il2CppObjectBase)item2.Equippable).TryCast<Equippable_Cuke>() != (Object)null) { num2++; VLog("'" + key + "' already consume-equippable"); continue; } string text = "none"; try { if ((Object)(object)item2.Equippable != (Object)null) { text = ((MemberInfo)((Object)item2.Equippable).GetIl2CppType()).Name; } } catch { text = "?"; } if (text != "none" && text != "Equippable_Viewmodel") { num4++; MelonLogger.Msg($"[Eat] '{key}' has functional equippable ({text}) — NOT wired."); } else { item2.EquipMode = item.EquipMode; item2.Equippable = item.Equippable; ((BaseItemDefinition)item2).EquippableData = ((BaseItemDefinition)item).EquippableData; bool flag = (Object)(object)item2.Equippable != (Object)null && (Object)(object)((Il2CppObjectBase)item2.Equippable).TryCast<Equippable_Cuke>() != (Object)null; num++; _wiredIds.Add(key); MelonLogger.Msg($"[Eat] '{key}' wired into the native drink flow (verify readback: {(flag ? "OK" : "FAILED")})."); } } MelonLogger.Msg($"[Eat] wiring complete: {num} wired, {num2} already native, {num4} skipped (functional equippable), {num3} missing, {_map.Count} mapped total."); } catch (Exception ex) { MelonLogger.Warning("[Eat] food wiring failed (will retry next session): " + ex.Message); _foodMadeDrinkable = false; } } public static void OnSessionEndedFoodWiring() { _foodMadeDrinkable = false; _wiredIds.Clear(); } internal static void VLog(string msg) { if (NeedsConfig.VerboseConsume) { MelonLogger.Msg("[Eat] " + msg); } } public static string BuildToast(string itemName, Restore r) { string text = itemName ?? ""; string[] pool; if (text.IndexOf("Pan Lasagna", StringComparison.OrdinalIgnoreCase) >= 0) { pool = DanLasagnaLines; } else if (text.IndexOf("Dan's", StringComparison.OrdinalIgnoreCase) >= 0 && text.IndexOf("Chili", StringComparison.OrdinalIgnoreCase) >= 0) { pool = DanChiliLines; } else if (text.IndexOf("Manuke", StringComparison.OrdinalIgnoreCase) >= 0) { pool = ManukeLines; } else if (text.IndexOf("Special", StringComparison.OrdinalIgnoreCase) >= 0) { pool = KyleSpecialLines; } else if (text.IndexOf("Taco", StringComparison.OrdinalIgnoreCase) >= 0) { pool = TacoLines; } else if (r.Thirst > r.Hunger && r.Thirst > 0f) { pool = DrinkLines; } else if (r.Hunger > 0f) { pool = EatLines; } else { if (!(r.Energy > 0f)) { return itemName; } pool = EnergyLines; } return Lines.Pick(pool, itemName); } } public static class ConsumeAudio { public enum ConsumeSoundKind { Crunch, Soft, Sip, Gulp, Slurp, Beer, Burrito, Native } private static AudioSource _source; private static AudioClip _nativeEat; private static AudioClip _capOpen; private static AudioClip _nativeCap; private static readonly List<AudioClip> _pool = new List<AudioClip>(); private static bool _nativeSearched; private static bool _poolTried; private static bool _capTried; private static int _lastPick = -1; private static Random _rng = new Random(20260809); private static readonly Dictionary<string, ConsumeSoundKind> _soundKinds = new Dictionary<string, ConsumeSoundKind>(StringComparer.OrdinalIgnoreCase) { ["brekkieroll"] = ConsumeSoundKind.Soft, ["tunasando"] = ConsumeSoundKind.Soft, ["delisando"] = ConsumeSoundKind.Soft, ["vendingsando"] = ConsumeSoundKind.Soft, ["porkbun"] = ConsumeSoundKind.Soft, ["pretzelbrat"] = ConsumeSoundKind.Soft, ["bakebrownie"] = ConsumeSoundKind.Soft, ["mingchicken"] = ConsumeSoundKind.Soft, ["rotisserie"] = ConsumeSoundKind.Soft, ["steakfrites"] = ConsumeSoundKind.Soft, ["smokedfish"] = ConsumeSoundKind.Soft, ["danlasagna"] = ConsumeSoundKind.Soft, ["krautslice"] = ConsumeSoundKind.Soft, ["almonddanish"] = ConsumeSoundKind.Soft, ["cremebrulee"] = ConsumeSoundKind.Soft, ["donut"] = ConsumeSoundKind.Soft, ["chili"] = ConsumeSoundKind.Soft, ["megabean"] = ConsumeSoundKind.Soft, ["fishnchips"] = ConsumeSoundKind.Crunch, ["croissant"] = ConsumeSoundKind.Crunch, ["sausageroll"] = ConsumeSoundKind.Crunch, ["theslop"] = ConsumeSoundKind.Slurp, ["instantramen"] = ConsumeSoundKind.Slurp, ["noodlecup"] = ConsumeSoundKind.Slurp, ["onionsoup"] = ConsumeSoundKind.Slurp, ["soupbowl"] = ConsumeSoundKind.Slurp, ["danchili"] = ConsumeSoundKind.Slurp, ["dinercoffee"] = ConsumeSoundKind.Sip, ["urncoffee"] = ConsumeSoundKind.Sip, ["flatwhite"] = ConsumeSoundKind.Sip, ["coatespresso"] = ConsumeSoundKind.Sip, ["lobbycoffee"] = ConsumeSoundKind.Sip, ["thermoscoffee"] = ConsumeSoundKind.Sip, ["churchcoffee"] = ConsumeSoundKind.Sip, ["jasminetea"] = ConsumeSoundKind.Sip, ["slushee"] = ConsumeSoundKind.Gulp, ["bottledwater"] = ConsumeSoundKind.Gulp, ["vodkasoda"] = ConsumeSoundKind.Gulp, ["canarycocktail"] = ConsumeSoundKind.Gulp, ["boxedwine"] = ConsumeSoundKind.Gulp, ["tallboy"] = ConsumeSoundKind.Beer, ["weissbier"] = ConsumeSoundKind.Beer, ["burroito"] = ConsumeSoundKind.Burrito }; private static readonly Dictionary<string, AudioClip[]> _catPools = new Dictionary<string, AudioClip[]>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> _catLast = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static AudioClip _burritoBite; private static bool _burritoTried; private static AudioClip _pillPop; private static bool _pillTried; private static AudioClip _blisterPop; private static bool _blisterTried; private static readonly string[] Tier1 = new string[4] { "chew", "munch", "chomp", "bite" }; private static readonly string[] Tier2 = new string[1] { "crunch" }; private static readonly string[] Tier3 = new string[2] { "meat", "eat" }; private static readonly string[] EatGuards = new string[10] { "death", "weather", "heart", "creat", "beat", "leather", "sweat", "theat", "feat", "repeat" }; public static void PlayCapOpen() { if (!NeedsConfig.ConsumeSound) { return; } try { EnsureBuilt(); AudioClip val = (Alive(_nativeCap) ? _nativeCap : _capOpen); string value = (Alive(_nativeCap) ? "native game clip" : "bundled CC0 recording"); bool flag = (Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source); if (flag && Alive(val)) { _source.pitch = 0.97f + 0.06f * (float)_rng.NextDouble(); _source.PlayOneShot(val, Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume)); MelonLogger.Msg($"[Audio] cap-open: PLAYING '{((Object)val).name}' ({value})"); } else { MelonLogger.Warning($"[Audio] cap-open: NOT PLAYED — source alive={flag}, clip loaded={Alive(val)}"); } } catch (Exception ex) { MelonLogger.Warning("[Audio] Cap-open sound failed: " + ex.Message); } } public static ConsumeSoundKind KindFor(string id) { if (!_soundKinds.TryGetValue(id ?? "", out var value)) { return ConsumeSoundKind.Native; } return value; } public static bool MutesNativeSlurp(string id) { ConsumeSoundKind consumeSoundKind = KindFor(id); if (consumeSoundKind != ConsumeSoundKind.Sip && consumeSoundKind != ConsumeSoundKind.Gulp) { return consumeSoundKind == ConsumeSoundKind.Beer; } return true; } public static bool HasDrinkSound(string id) { return MutesNativeSlurp(id); } public static void PlayConsume(string id) { switch (KindFor(id)) { case ConsumeSoundKind.Burrito: PlayBurritoBite(); break; case ConsumeSoundKind.Soft: PlayCategory("softchew", 3, 0.94f, 0.12f); break; case ConsumeSoundKind.Sip: PlayCategory("sip", 3, 0.96f, 0.08f); break; case ConsumeSoundKind.Gulp: PlayCategory("gulp", 3, 0.94f, 0.12f); break; case ConsumeSoundKind.Slurp: PlayCategory("slurp", 3, 0.95f, 0.1f); break; case ConsumeSoundKind.Beer: PlayCategory("beer", 2, 0.95f, 0.1f); break; default: PlayEat(); break; } } private static void PlayCategory(string baseName, int count, float basePitch, float pitchSpread) { if (!NeedsConfig.ConsumeSound) { return; } try { EnsureBuilt(); if (!_catPools.TryGetValue(baseName, out var value) || value == null) { List<AudioClip> list = new List<AudioClip>(count); for (int i = 1; i <= count; i++) { AudioClip val = LoadEmbeddedWav($"S1HungerThirst.Assets.{baseName}{i}.wav", $"S1HT_{baseName}{i}"); if ((Object)(object)val != (Object)null) { list.Add(val); } } value = list.ToArray(); _catPools[baseName] = value; MelonLogger.Msg($"[Audio] category '{baseName}': {value.Length}/{count} variant(s) loaded."); } if ((Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source) && value.Length != 0) { int value2; int num = (_catLast.TryGetValue(baseName, out value2) ? value2 : (-1)); int num2; do { num2 = _rng.Next(value.Length); } while (value.Length > 1 && num2 == num); _catLast[baseName] = num2; AudioClip val2 = value[num2]; if (Alive(val2)) { _source.pitch = basePitch + pitchSpread * (float)_rng.NextDouble(); _source.PlayOneShot(val2, Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume)); MelonLogger.Msg($"[Audio] category '{baseName}': PLAYING variant {num2 + 1}"); } } } catch (Exception ex) { MelonLogger.Warning("[Audio] category '" + baseName + "' failed: " + ex.Message); } } private static AudioClip LoadEmbeddedWav(string resource, string clipName) { try { using Stream stream = typeof(ConsumeAudio).Assembly.GetManifestResourceStream(resource); if (stream == null) { MelonLogger.Warning("[Audio] bundled '" + resource + "' not found."); return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } return WavToClip(array, clipName); } catch (Exception ex) { MelonLogger.Warning("[Audio] '" + resource + "' load failed: " + ex.Message); return null; } } public static void PlayBurritoBite() { if (!NeedsConfig.ConsumeSound) { MelonLogger.Msg("[Audio] burrito bite: ConsumeSound disabled, no sound."); return; } try { EnsureBuilt(); if (!_burritoTried || !Alive(_burritoBite)) { LoadBurritoBite(); } if ((Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source) && Alive(_burritoBite)) { _source.pitch = 0.95f + 0.1f * (float)_rng.NextDouble(); _source.PlayOneShot(_burritoBite, Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume)); MelonLogger.Msg("[Audio] burrito bite: PLAYING '" + ((Object)_burritoBite).name + "'"); } else { MelonLogger.Msg("[Audio] burrito bite unavailable — falling back to the generic bite."); PlayEat(); } } catch (Exception ex) { MelonLogger.Warning("[Audio] burrito bite failed: " + ex.Message); } } private static void LoadBurritoBite() { _burritoTried = true; try { using Stream stream = typeof(ConsumeAudio).Assembly.GetManifestResourceStream("S1HungerThirst.Assets.burritobite.wav"); if (stream == null) { MelonLogger.Warning("[Audio] bundled burritobite.wav not found in assembly."); return; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } _burritoBite = WavToClip(array, "S1HT_BurritoBite"); if ((Object)(object)_burritoBite != (Object)null) { MelonLogger.Msg($"[Audio] BURRITO BITE = bundled recording ({_burritoBite.length:0.00}s, freesound_community via Pixabay)."); } } catch (Exception ex) { MelonLogger.Warning("[Audio] burrito bite sample failed to load: " + ex.Message); } } public static void PlayEat() { if (!NeedsConfig.ConsumeSound) { MelonLogger.Msg("[Audio] bite: ConsumeSound disabled, no sound."); return; } try { EnsureBuilt(); AudioClip val = null; string value = null; if ((NeedsConfig.EatClipName ?? "").Trim().Length > 0 && Alive(_nativeEat)) { val = _nativeEat; value = "pinned native clip"; } else if (_pool.Count > 0) { val = PickFromPool(); value = "bundled CC0 recording"; } else if (Alive(_nativeEat)) { val = _nativeEat; value = "native game clip"; } bool flag = (Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source); if (flag && (Object)(object)val != (Object)null) { float num = Mathf.Clamp(NeedsConfig.EatPitchVariance, 0f, 0.5f); _source.pitch = 1f - num * 0.5f + num * (float)_rng.NextDouble(); float num2 = Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume); _source.PlayOneShot(val, num2); MelonLogger.Msg($"[Audio] bite: PLAYING '{((Object)val).name}' ({value}, {val.length:0.00}s, vol={num2:0.00}, pitch={_source.pitch:0.00})"); } else { MelonLogger.Warning($"[Audio] bite: NOT PLAYED — source alive={flag}, pool={_pool.Count}, native={(Alive(_nativeEat) ? ((Object)_nativeEat).name : "none")}"); } } catch (Exception ex) { MelonLogger.Warning("[Audio] Eat sound failed: " + ex.Message); } } private static AudioClip PickFromPool() { if (_pool.Count == 1) { return _pool[0]; } int num; do { num = _rng.Next(_pool.Count); } while (num == _lastPick); _lastPick = num; return _pool[num]; } private static bool Alive(AudioClip c) { if ((Object)(object)c != (Object)null) { return Object.op_Implicit((Object)(object)c); } return false; } public static void Teardown() { try { if ((Object)(object)_source != (Object)null) { Object.Destroy((Object)(object)((Component)_source).gameObject); } } catch { } _source = null; _nativeSearched = false; _nativeEat = null; } private static void EnsureBuilt() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_source == (Object)null || !Object.op_Implicit((Object)(object)_source)) { _source = new GameObject("S1HT_Audio").AddComponent<AudioSource>(); _source.playOnAwake = false; _source.spatialBlend = 0f; } if (!_poolTried || PoolDead()) { LoadPool(); } if (!_capTried || !Alive(_capOpen)) { LoadCapOpen(); } if (!_nativeSearched) { FindNativeEatClip(); } } public static void PlayPillPop() { if (!NeedsConfig.ConsumeSound) { return; } try { EnsureBuilt(); if (!_pillTried || !Alive(_pillPop)) { LoadPillPop(); } if ((Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source) && Alive(_pillPop)) { _source.pitch = 0.96f + 0.08f * (float)_rng.NextDouble(); _source.PlayOneShot(_pillPop, Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume)); MelonLogger.Msg("[Audio] pill-pop: PLAYING '" + ((Object)_pillPop).name + "'"); } } catch (Exception ex) { MelonLogger.Warning("[Audio] pill-pop failed: " + ex.Message); } } private static void LoadPillPop() { _pillTried = true; try { using Stream stream = typeof(ConsumeAudio).Assembly.GetManifestResourceStream("S1HungerThirst.Assets.pills.wav"); if (stream == null) { MelonLogger.Warning("[Audio] bundled pills.wav not found in assembly."); return; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } _pillPop = WavToClip(array, "S1HT_PillPop"); if ((Object)(object)_pillPop != (Object)null) { MelonLogger.Msg($"[Audio] PILL SOUND = bundled recording ({_pillPop.length:0.00}s, Rudmer_Rotteveel via Pixabay)."); } } catch (Exception ex) { MelonLogger.Warning("[Audio] pills sample failed to load: " + ex.Message); } } public static void PlayBlisterPop() { if (!NeedsConfig.ConsumeSound) { return; } try { EnsureBuilt(); if (!_blisterTried || !Alive(_blisterPop)) { LoadBlisterPop(); } if ((Object)(object)_source != (Object)null && Object.op_Implicit((Object)(object)_source) && Alive(_blisterPop)) { _source.pitch = 0.96f + 0.08f * (float)_rng.NextDouble(); _source.PlayOneShot(_blisterPop, Mathf.Clamp01(NeedsConfig.ConsumeSoundVolume)); MelonLogger.Msg("[Audio] blister-pop: PLAYING '" + ((Object)_blisterPop).name + "'"); } } catch (Exception ex) { MelonLogger.Warning("[Audio] blister-pop failed: " + ex.Message); } } private static void LoadBlisterPop() { _blisterTried = true; try { using Stream stream = typeof(ConsumeAudio).Assembly.GetManifestResourceStream("S1HungerThirst.Assets.blister.wav"); if (stream == null) { MelonLogger.Warning("[Audio] bundled blister.wav not found in assembly."); return; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } _blisterPop = WavToClip(array, "S1HT_BlisterPop"); if ((Object)(object)_blisterPop != (Object)null) { MelonLogger.Msg($"[Audio] BLISTER SOUND = bundled recording ({_blisterPop.length:0.00}s, spinopel via Pixabay)."); } } catch (Exception ex) { MelonLogger.Warning("[Audio] blister sample failed to load: " + ex.Message); } } private static void LoadCapOpen() { _capTried = true; try { using Stream stream = typeof(ConsumeAudio).Assembly.GetManifestResourceStream("S1HungerThirst.Assets.capopen.wav"); if (stream == null) { MelonLogger.Warning("[Audio] bundled capopen.wav not found in assembly."); return; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } _capOpen = WavToClip(array, "S1HT_CapOpen"); if ((Object)(object)_capOpen != (Object)null) { MelonLogger.Msg($"[Audio] CAP-OPEN SOUND = bundled CC0 recording ({_capOpen.length:0.00}s, OwlishMedia)."); } } catch (Exception ex) { MelonLogger.Warning("[Audio] capopen sample failed to load: " + ex.Message); } } private static bool PoolDead() { for (int i = 0; i < _pool.Count; i++) { if (!Alive(_pool[i])) { return true; } } return _pool.Count == 0; } private static void LoadPool() { _poolTried = true; _pool.Clear(); _lastPick = -1; Assembly assembly = typeof(ConsumeAudio).Assembly; for (int i = 1; i <= 8; i++) { string text = $"S1HungerThirst.Assets.eat{i}.wav"; try { using Stream stream = assembly.GetManifestResourceStream(text); if (stream == null) { continue; } byte[] array = new byte[stream.Length]; int num; for (int j = 0; j < array.Length; j += num) { num = stream.Read(array, j, array.Length - j); if (num <= 0) { break; } } AudioClip val = WavToClip(array, $"S1HT_Eat{i}"); if ((Object)(object)val != (Object)null) { _pool.Add(val); } } catch (Exception ex) { MelonLogger.Warning("[Audio] bundled '" + text + "' failed to load: " + ex.Message); } } if (_pool.Count > 0) { MelonLogger.Msg($"[Audio] EAT SOUND = pool of {_pool.Count} bundled CC0 bite recordings (cucumber/toast, by cogitollc)."); } else { MelonLogger.Warning("[Audio] NO bundled eat recordings loaded — will try a native game clip."); } } private static AudioClip WavToClip(byte[] b, string name) { if (b.Length < 44 || b[0] != 82 || b[1] != 73 || b[2] != 70 || b[3] != 70 || b[8] != 87 || b[9] != 65 || b[10] != 86 || b[11] != 69) { MelonLogger.Warning("[Audio] '" + name + "' is not a RIFF/WAVE file."); return null; } int num = 0; int num2 = 0; int num3 = 0; int num4 = -1; int num5 = 0; int num6; for (int i = 12; i + 8 <= b.Length; i += 8 + num6 + (num6 & 1)) { string text = Encoding.ASCII.GetString(b, i, 4); num6 = BitConverter.ToInt32(b, i + 4); if (text == "fmt ") { int num7 = BitConverter.ToInt16(b, i + 8); num = BitConverter.ToInt16(b, i + 10); num2 = BitConverter.ToInt32(b, i + 12); num3 = BitConverter.ToInt16(b, i + 22); if (num7 != 1 || num3 != 16) { MelonLogger.Warning($"[Audio] '{name}' format unsupported (fmt={num7}, bits={num3})."); return null; } } else if (text == "data") { num4 = i + 8; num5 = num6; } } if (num4 < 0 || num <= 0 || num2 <= 0) { return null; } if (num4 + num5 > b.Length) { num5 = b.Length - num4; } int num8 = num5 / (2 * num); float[] array = new float[num8]; for (int j = 0; j < num8; j++) { float num9 = 0f; for (int k = 0; k < num; k++) { num9 += (float)BitConverter.ToInt16(b, num4 + (j * num + k) * 2) / 32768f; } array[j] = num9 / (float)num; } AudioClip obj = AudioClip.Create(name, num8, 1, num2, false); obj.SetData(Il2CppStructArray<float>.op_Implicit(array), 0); ((Object)obj).hideFlags = (HideFlags)32; return obj; } private static void FindNativeEatClip() { _nativeSearched = true; try { Il2CppReferenceArray<Object> val = Resources.FindObjectsOfTypeAll(Il2CppType.Of<AudioClip>()); string text = (NeedsConfig.EatClipName ?? "").Trim(); AudioClip val2 = null; int num = 99; int num2 = 0; for (int i = 0; i < ((Il2CppArrayBase<Object>)(object)val).Length; i++) { AudioClip val3 = ((Il2CppObjectBase)((Il2CppArrayBase<Object>)(object)val)[i]).TryCast<AudioClip>(); if (!((Object)(object)val3 == (Object)null)) { string text2 = ((Object)val3).name.ToLowerInvariant(); if ((Object)(object)_nativeCap == (Object)null && val3.length >= 0.1f && val3.length <= 2f && (text2.Contains("bottlecap") || text2.Contains("bottle_open") || text2.Contains("unscrew") || (text2.Contains("bottle") && text2.Contains("open")))) { _nativeCap = val3; MelonLogger.Msg("[Audio] native bottle-open clip found: '" + ((Object)val3).name + "' — used for mouthwash."); } if (text.Length > 0 && string.Equals(((Object)val3).name, text, StringComparison.OrdinalIgnoreCase)) { val2 = val3; num = 0; break; } int num3 = ScoreName(text2); if (num3 < 99 && NeedsConfig.VerboseConsume && num2 < 25) { MelonLogger.Msg($"[Audio] candidate clip: '{((Object)val3).name}' ({val3.length:0.00}s, tier {num3})"); num2++; } if (num3 < num && val3.length >= 0.15f && val3.length <= 3f) { val2 = val3; num = num3; } } } if ((Object)(object)val2 != (Object)null) { _nativeEat = val2; MelonLogger.Msg($"[Audio] native eat clip available: '{((Object)val2).name}' ({val2.length:0.00}s, tier {num})."); } } catch (Exception ex) { MelonLogger.Warning("[Audio] Native clip search failed: " + ex.Message); } } private static int ScoreName(string n) { string[] tier = Tier1; foreach (string value in tier) { if (n.Contains(value)) { return 1; } } tier = Tier2; foreach (string value2 in tier) { if (n.Contains(value2)) { return 2; } } tier = Tier3; foreach (string text in tier) { if (!n.Contains(text)) { continue; } if (text == "eat") { bool flag = false; string[] eatGuards = EatGuards; foreach (string value3 in eatGuards) { if (n.Contains(value3)) { flag = true; break; } } if (flag) { continue; } } return 3; } return 99; } } public static class ConsumeWatcher { private const string MouthwashId = "mouthwash"; private static IntPtr _vmPtr = IntPtr.Zero; private static Equippable_Cuke _vm; private static bool _vmIsFood; private static bool _vmIsMouthwash; private static bool _vmIsWired; private static bool _vmIsCuke; private static bool _vmIsGasoline; private static bool _vmIsFx; private static bool _vmFxArmed; private static bool _vmIsPill; private static bool _vmHasOwnTrash; private static Restore _vmRestore; private static string _vmName; private static string _vmId; private static bool _wasDrinking; private static ProductDefinition _stashedPseudo; private static bool _pseudoStashed; private static ItemSlot _watchedSlot; private static int _watchedQty = -1; private static readonly HashSet<IntPtr> _trashBefore = new HashSet<IntPtr>(); private static float _trashWindowUntil; private static int _probeIndex; private static float _nextProbeAt; private static bool _patchProven; private static readonly float[] ProbeDelays = new float[4] { 0.5f, 1.25f, 2.5f, 4f }; private static readonly string[] FxTakenLines = new string[3] { "Straight, no mixer.", "Neat. As intended by nobody.", "Down raw. Here it comes." }; private static readonly string[] FxHeldLines = new string[3] { "Already on something. It can wait.", "One thing at a time.", "The current high called dibs." }; private static readonly string[] PurgeUpLines = new string[3] { "That came straight back up.", "Nope. Straight back out.", "The body voted no on the mouthwash." }; private static readonly string[] PurgeClearedLines = new string[3] { "High's gone with it.", "Sober now. The hard way.", "Whatever that was, it's in the gutter now." }; private static readonly string[] PurgePointlessLines = new string[3] { "Drank mouthwash for no reason. Great.", "Minty. Pointless. Regrettable.", "That achieved nothing except the puking." }; public static bool EatWindowActive { get { if (_trashWindowUntil > 0f) { return Time.unscaledTime <= _trashWindowUntil; } return false; } } private static void ClearVmTracking() { _vmPtr = IntPtr.Zero; _vm = null; _vmIsFood = false; _vmIsWired = false; _vmIsCuke = false; _vmIsGasoline = false; _vmIsFx = false; _vmFxArmed = false; _vmIsPill = false; _vmHasOwnTrash = false; _wasDrinking = false; _stashedPseudo = null; _pseudoStashed = false; _watchedSlot = null; _watchedQty = -1; } public static void Reset() { ClearVmTracking(); _trashBefore.Clear(); _trashWindowUntil = 0f; _probeIndex = 0; } public static void Tick() { //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) SweepTrash(); PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { ClearVmTracking(); return; } try { if (_watchedSlot != null && _watchedQty >= 0) { int num = -1; try { num = _watchedSlot.Quantity; } catch { _watchedSlot = null; } if (num >= 0 && num < _watchedQty) { int num2 = _watchedQty - num; Restore vmRestore = _vmRestore; NeedsState.Replenish(vmRestore.Hunger * (float)num2, vmRestore.Thirst * (float)num2, vmRestore.Energy * (float)num2); if (_vmIsMouthwash) { OnMouthwashConsumed(); } else if (_vmIsGasoline) { OnGasolineConsumed(); } else if (_vmIsFx) { NeedsHud.Toast(_vmFxArmed ? Lines.Pick(FxTakenLines) : Lines.Pick(FxHeldLines), _vmFxArmed ? NeedsHud.ToastGood : NeedsHud.ToastNeutral); Diag.Log($"[FX] '{_vmId}' consumed — {(_vmFxArmed ? "effect applied via native flow (registered real-product clone)" : "withheld (real high active)")}."); } else { NeedsHud.Toast(ConsumableMap.BuildToast(_vmName, vmRestore), NeedsHud.ToastGood); if (TacoItem.IsTacoId(_vmId)) { TacoItem.OnTacoEaten(_vmId); } } MelonLogger.Msg($"[Eat] consumed '{_vmId}' x{num2} — restore applied (h={vmRestore.Hunger} t={vmRestore.Thirst} e={vmRestore.Energy})"); } if (num >= 0) { _watchedQty = num; } } } catch (Exception ex) { MelonLogger.Warning("[Eat] quantity watch error: " + ex.Message); } Equippable_Cuke val = null; try { Equippable equippable = instance.Equippable; val = (((Object)(object)equippable != (Object)null) ? ((Il2CppObjectBase)equippable).TryCast<Equippable_Cuke>() : null); } catch { } if ((Object)(object)val == (Object)null) { if (_vmPtr != IntPtr.Zero) { RestorePseudo(); ClearVmTracking(); } return; } if (((Il2CppObjectBase)val).Pointer != _vmPtr) { RestorePseudo(); _vmPtr = ((Il2CppObjectBase)val).Pointer; _vm = val; _wasDrinking = false; ItemDefinition val2 = ((instance.EquippedItem != null) ? instance.EquippedItem.Definition : null); _vmId = (((Object)(object)val2 != (Object)null) ? ((BaseItemDefinition)val2).ID : "?"); _vmName = (((Object)(object)val2 != (Object)null) ? ((BaseItemDefinition)val2).Name : "?"); bool flag = ConsumableMap.TryGet(_vmId, out _vmRestore); if (!flag) { _vmRestore = new Restore(0f, 20f); } _vmIsMouthwash = string.Equals(_vmId, "mouthwash", StringComparison.OrdinalIgnoreCase); _vmIsGasoline = string.Equals(_vmId, "gasoline", StringComparison.OrdinalIgnoreCase); _vmIsFx = IngredientEffects.IsFxIngredient(_vmId); _vmIsPill = _vmIsFx && IngredientEffects.IsPill(_vmId); _vmIsFood = !_vmIsMouthwash && !_vmIsGasoline && !_vmIsFx && flag && ConsumableMap.IsFood(_vmRestore); _vmIsWired = ConsumableMap.WasWired(_vmId); _vmIsCuke = string.Equals(_vmId, "cuke", StringComparison.OrdinalIgnoreCase); _vmFxArmed = false; _vmHasOwnTrash = false; _watchedSlot = null; _watchedQty = -1; try { _watchedSlot = (ItemSlot)(object)instance.equippedSlot; if (_watchedSlot != null) { _watchedQty = _watchedSlot.Quantity; } } catch { } ConsumableMap.VLog($"equipped '{_vmId}' via cuke flow (food={_vmIsFood}, qty={_watchedQty})"); try { if (NeedsConfig.PreserveDrugEffects) { val.ClearDrugEffects = false; } if (_vmIsWired || _vmIsCuke) { float baseEnergyGain = val.BaseEnergyGain; float minEnergyGain = val.MinEnergyGain; val.BaseEnergyGain = 0f; val.MinEnergyGain = 0f; if (_vmIsWired) { val.HealthGain = 0f; } if (baseEnergyGain != 0f || minEnergyGain != 0f) { ConsumableMap.VLog($"native stim payload zeroed on '{_vmId}' (BaseEnergyGain {baseEnergyGain:0.#}->0, MinEnergyGain {minEnergyGain:0.#}->0) — heart racing is energy-drink-only"); } } if (ConsumableMap.WasWired(_vmId)) { ApplyFoodPresentation(val, val2, muteOpen: true, _vmIsFood || _vmIsPill || ConsumeAudio.MutesNativeSlurp(_vmId)); if (_vmIsMouthwash || _vmIsFx) { _vmHasOwnTrash = SwapTrashPrefab(val, _vmId); } } } catch (Exception ex2) { MelonLogger.Warning("[Eat] presentation error: " + ex2.Message); } } try { bool flag2 = (Object)(object)Player.Local != (Object)null && Player.Local.ConsumedProduct != null; bool flag3 = false; try { ProductItemInstance val3 = (flag2 ? Player.Local.ConsumedProduct : null); flag3 = val3 != null && (Object)(object)((ItemInstance)val3).Definition != (Object)null && (((BaseItemDefinition)((ItemInstance)val3).Definition).ID ?? "").StartsWith("s1ht_fx_", StringComparison.OrdinalIgnoreCase); } catch { } if (_vmIsFx && (!flag2 || flag3 || !NeedsConfig.PreserveDrugEffects)) { ProductDefinition fxProduct = IngredientEffects.GetFxProduct(_vmId); if ((Object)(object)fxProduct != (Object)null && ((Object)(object)_vm.PseudoProduct == (Object)null || ((Il2CppObjectBase)_vm.PseudoProduct).Pointer != ((Il2CppObjectBase)fxProduct).Pointer)) { _vm.PseudoProduct = fxProduct; _vmFxArmed = true; ConsumableMap.VLog($"ingredient effect armed: '{_vmId}' → product '{((BaseItemDefinition)fxProduct).ID}' (native flow)"); } } else if (_vmIsWired || _vmIsCuke) { if ((Object)(object)_vm.PseudoProduct != (Object)null) { _vm.PseudoProduct = null; _vmFxArmed = false; ConsumableMap.VLog(_vmIsCuke ? "cuke pseudo-product removed (plain thirst drink — no stim)" : (_vmIsFx ? "real high active — ingredient effect held back (preservation)" : "wired item — pseudo-product disarmed (no hidden energy kick)")); } } else if (NeedsConfig.PreserveDrugEffects && flag2 && !_pseudoStashed && (Object)(object)_vm.PseudoProduct != (Object)null) { _stashedPseudo = _vm.PseudoProduct; _vm.PseudoProduct = null; _pseudoStashed = true; ConsumableMap.VLog("high — pseudo-product disarmed for this equip"); } else if (_pseudoStashed && !flag2 && !_vmIsWired && !_vmIsCuke) { RestorePseudo(); } bool isDrinking = _vm.IsDrinking; if (isDrinking && !_wasDrinking) { ConsumableMap.VLog("bite/sip started on '" + _vmId + "' — " + (_vmIsMouthwash ? "cap-open, native slurp, purge on completion" : (_vmIsFood ? "CHOMP" : "native slurp"))); if (_vmIsMouthwash) { ConsumeAudio.PlayCapOpen(); } else if (_vmIsFood) { ConsumeAudio.PlayConsume(_vmId); SnapshotTrash(); } else if (_vmIsFx) { if (_vmIsPill) { if (IngredientEffects.IsBottlePill(_vmId)) { ConsumeAudio.PlayPillPop(); } else { ConsumeAudio.PlayBlisterPop(); } } if (!_vmHasOwnTrash) { SnapshotTrash(); } } else if (_vmIsGasoline && !_vmHasOwnTrash) { SnapshotTrash(); } else if (ConsumeAudio.HasDrinkSound(_vmId)) { ConsumeAudio.PlayConsume(_vmId); if (!IsCannedDrink(_vmId)) { SnapshotTrash(); } } } _wasDrinking = isDrinking; } catch (Exception ex3) { MelonLogger.Warning("[Eat] watcher error: " + ex3.Message); } SweepTrash(); } public static void NotifyTrashSpawned(TrashItem t) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (!EatWindowActive || (Object)(object)t == (Object)null) { return; } try { string a = ""; try { a = t.ID ?? ""; } catch { } if (string.Equals(a, "plantscrap", StringComparison.OrdinalIgnoreCase)) { return; } Player local = Player.Local; if ((Object)(object)local != (Object)null) { Vector3 val = ((Component)t).transform.position - ((Component)local).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 64f) { return; } } Object.Destroy((Object)(object)((Component)t).gameObject); if (!_patchProven) { _patchProven = true; MelonLogger.Msg("[Eat] trash patch is live — probe scans disabled from here on."); } _trashWindowUntil = 0f; _probeIndex = ProbeDelays.Length; ConsumableMap.VLog("thrown can culled at spawn (patched path, no scan)"); } catch (Exception ex) { MelonLogger.Warning("[Eat] spawn-cull failed: " + ex.Message); } } private static bool IsCannedDrink(string id) { return string.Equals(id, "tallboy", StringComparison.OrdinalIgnoreCase); } private static void SnapshotTrash() { float unscaledTime = Time.unscaledTime; _trashWindowUntil = unscaledTime + 6f; _probeIndex = 0; _nextProbeAt = unscaledTime + ProbeDelays[0]; if (_patchProven) { _probeIndex = ProbeDelays.Length; return; } _trashBefore.Clear(); try { Il2CppArrayBase<TrashItem> val = Object.FindObjectsOfType<TrashItem>(); for (int i = 0; i < val.Length; i++) { if ((Object)(object)val[i] != (Object)null) { _trashBefore.Add(((Il2CppObjectBase)val[i]).Pointer); } } } catch (Exception ex) { MelonLogger.Warning("[Eat] trash snapshot failed: " + ex.Message); } } private static void SweepTrash() { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (_trashWindowUntil <= 0f) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime > _trashWindowUntil) { _trashWindowUntil = 0f; } else { if (_patchProven || _probeIndex >= ProbeDelays.Length || unscaledTime < _nextProbeAt) { return; } _probeIndex++; if (_probeIndex < ProbeDelays.Length) { _nextProbeAt = _trashWindowUntil - 6f + ProbeDelays[_probeIndex]; } try { Player local = Player.Local; if ((Object)(object)local == (Object)null) { return; } Vector3 position = ((Component)local).transform.position; Il2CppArrayBase<TrashItem> val = Object.FindObjectsOfType<TrashItem>(); for (int i = 0; i < val.Length; i++) { TrashItem val2 = val[i]; if ((Object)(object)val2 == (Object)null || _trashBefore.Contains(((Il2CppObjectBase)val2).Pointer)) { continue; } string a = ""; try { a = val2.ID ?? ""; } catch { } if (!string.Equals(a, "plantscrap", StringComparison.OrdinalIgnoreCase)) { Vector3 val3 = ((Component)val2).transform.position - position; if (!(((Vector3)(ref val3)).sqrMagnitude > 64f)) { Object.Destroy((Object)(object)((Component)val2).gameObject); ConsumableMap.VLog($"thrown can culled by probe scan #{_probeIndex}"); _trashWindowUntil = 0f; break; } } } } catch (Exception ex) { MelonLogger.Warning("[Eat] trash probe failed: " + ex.Message); _trashWindowUntil = 0f; } } } private static void OnMouthwashConsumed() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) try { Player local = Player.Local; bool flag = (Object)(object)local != (Object)null && local.ConsumedProduct != null; TriggerNativePuke(local); NeedsHud.Toast(Lines.Pick(PurgeUpLines), NeedsHud.ToastWarn); if (NeedsConfig.MouthwashClearsEffects && flag) { DrugEffects.Reset(); local.ClearProduct(); NeedsHud.Toast(Lines.Pick(PurgeClearedLines), NeedsHud.ToastNeutral); MelonLogger.Msg("[Purge] mouthwash drink completed while high — native TriggerSick + ClearProduct()."); } else { NeedsHud.Toast(Lines.Pick(PurgePointlessLines), NeedsHud.ToastNeutral); MelonLogger.Msg("[Purge] mouthwash drink completed while sober — puked, nothing to clear."); } } catch (Exception ex) { MelonLogger.Warning("[Purge] mouthwash clear failed: " + ex.Message); } } private static void OnGasolineConsumed() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) try { Player local = Player.Local; if ((Object)(object)local == (Object)null) { return; } TriggerNativePuke(local); NeedsHud.Toast("That was petrol.", NeedsHud.ToastBad); PlayerHealth health = local.Health; if (NeedsConfig.GasolineLethal) { MelonLogger.Msg("[Gasoline] consumed — lethal path (SetHealth 0 + SendDie)."); if ((Object)(object)health != (Object)null && health.IsAlive) { health.SetHealth(0f); try { health.SendDie(); return; } catch (Exception ex) { MelonLogger.Warning("[Gasoline] SendDie failed: " + ex.Message); return; } } } else { MelonLogger.Msg("[Gasoline] consumed — knockout path (health floored to 10, energy 0, blackout)."); if ((Object)(object)health != (Object)null && health.IsAlive && health.CurrentHealth > 10f) { health.SetHealth(10f); } NeedsState.SetEnergy(0f); Penalties.Blackout(6f); NeedsHud.Toast("Lights out.", NeedsHud.ToastBad); } } catch (Exception ex2) { MelonLogger.Warning("[Gasoline] consume handling failed: " + ex2.Message); } } private static void TriggerNativePuke(Player player) { try { AvatarEffects val = (((Object)(object)player != (Object)null && (Object)(object)player.Avatar != (Object)null) ? player.Avatar.Effects : null); if ((Object)(object)val != (Object)null) { val.TriggerSick(true); ConsumableMap.VLog("native TriggerSick fired (VomitParticles + VomitSound)"); } else { MelonLogger.Warning("[Purge] AvatarEffects not found — puke visual/sound skipped."); } } catch (Exception ex) { MelonLogger.Warning("[Purge] TriggerSick failed: " + ex.Message); } } private static bool SwapTrashPrefab(Equippable_Cuke vm, string itemId) { try { TrashManager instance = NetworkSingleton<TrashManager>.Instance; Il2CppReferenceArray<TrashItem> val = (((Object)(object)instance != (Object)null) ? instance.TrashPrefabs : null); if (val == null) { return false; } string text = (itemId ?? "").ToLowerInvariant(); if (text.Length == 0) { return false; } for (int i = 0; i < ((Il2CppArrayBase<TrashItem>)(object)val).Length; i++) { TrashItem val2 = ((Il2CppArrayBase<TrashItem>)(object)val)[i]; if (!((Object)(object)val2 == (Object)null) && (val2.ID + " " + ((Object)((Component)val2).gameObject).name).ToLowerInvariant().Contains(text)) { vm.TrashPrefab = val2; ConsumableMap.VLog($"trash prefab swapped to '{val2.ID}' (matches '{itemId}')"); return true; } } ConsumableMap.VLog("no '" + itemId + "' trash prefab in TrashManager — borrowed can handling applies"); return false; } catch (Exception ex) { MelonLogger.Warning("[Eat] trash prefab swap failed: " + ex.Message); return false; } } private static void RestorePseudo() { try { if (_pseudoStashed && (Object)(object)_vm != (Object)null && (Object)(object)_stashedPseudo != (Object)null) { _vm.PseudoProduct = _stashedPseudo; } } catch { } _stashedPseudo = null; _pseudoStashed = false; } private static void ApplyFoodPresentation(Equippable_Cuke vm, ItemDefinition def, bool muteOpen = true, bool muteSlurp = true) { //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Expected O, but got Unknown //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) int num = 0; int layer = ((Component)vm).gameObject.layer; Il2CppArrayBase<MeshRenderer> componentsInChildren = ((Component)vm).GetComponentsInChildren<MeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (!((Object)(object)componentsInChildren[i] == (Object)null)) { layer = ((Component)componentsInChildren[i]).gameObject.layer; ((Renderer)componentsInChildren[i]).enabled = false; num++; } } try { if (muteOpen && (Object)(object)vm.OpenSound != (Object)null) { vm.OpenSound.VolumeMultiplier = 0f; } if (muteSlurp && (Object)(object)vm.SlurpSound != (Object)null) { vm.SlurpSound.VolumeMultiplier = 0f; } } catch { } bool flag = false; string text = "none"; try { for (int num2 = ((Component)vm).transform.childCount - 1; num2 >= 0; num2--) { Transform child = ((Component)vm).transform.GetChild(num2); if ((Object)(object)child != (Object)null && (((Object)((Component)child).gameObject).name ?? "").StartsWith("S1HT_")) { Object.Destroy((Object)(object)((Component)child).gameObject); } } Renderer materialDonor = null; Il2CppArrayBase<MeshRenderer> componentsInChildren2 = ((Component)vm).GetComponentsInChildren<MeshRenderer>(true); if (componentsInChildren2.Length > 0) { materialDonor = (Renderer)(object)componentsInChildren2[0]; } if ((Object)(object)def != (Object)null && ItemModels.TryResolve(((BaseItemDefinition)def).ID, out var mesh, out var matKey, out var look, out var handEuler)) { GameObject val = ItemModels.BuildArchetypeHandObject(mesh, matKey, look, materialDonor, layer, handEuler); if ((Object)(object)val != (Object)null) { val.transform.SetParent(((Component)vm).transform, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.Euler(handEuler); flag = true; text = $"archetype mesh '{((Object)mesh).name}' (mat '{matKey}', rot {handEuler})"; } } if (!flag && (Object)(object)def != (Object)null) { string desc; GameObject val2 = ItemModels.BuildFromStoredItem(def, layer, out desc); if ((Object)(object)val2 != (Object)null) { val2.transform.SetParent(((Component)vm).transform, false); val2.transform.localPosition = Vector3.zero; flag = true; text = desc; } } if (!flag && (Object)(object)def != (Object)null && (Object)(object)((BaseItemDefinition)def).Icon != (Object)null) { GameObject val3 = new GameObject("S1HT_FoodVisual"); val3.transform.SetParent(((Component)vm).transform, false); val3.layer = layer; SpriteRenderer obj2 = val3.AddComponent<SpriteRenderer>(); obj2.sprite = ((BaseItemDefinition)def).Icon; Bounds bounds = obj2.sprite.bounds; Vector3 size = ((Bounds)(ref bounds)).size; float num3 = Mathf.Max(new float[3] { size.x, size.y, 0.01f }); val3.transform.localScale = Vector3.one * (0.16f / num3); val3.transform.localPosition = Vector3.zero; flag = true; text = "flat icon (no prop meshes on this item)"; } } catch (Exception ex) { MelonLogger.Warning("[Eat] food visual failed: " + ex.Message); } try { ItemModels.NoteHandModel(((Object)(object)def != (Object)null) ? ((BaseItemDefinition)def).ID : null, flag ? text : "NOTHING (all sources failed)"); } catch { } ConsumableMap.VLog($"food presentation: {num} can renderer(s) hidden, sounds muted, visual {(flag ? text : "unavailable")}"); } } public static class DanDish { public const string ChiliId = "danchili"; public const string LasagnaId = "danlasagna"; private const string DanNpcId = "dan_samwell"; private const string ReaskChoiceText = "About what I'm growing..."; private const string ReaskAdmitGuid = "S1HT_DAN_ADMIT"; private const string ReaskDenyGuid = "S1HT_DAN_DENY"; private const string ReaskExitGuid = "S1HT_DAN_NVM"; private const string MenuChoiceText = "Ask about lunch"; private const string MenuBuyGuid = "S1HT_DAN_BUY"; private const string MenuExitGuid = "S1HT_DAN_BYE"; private static bool _armed; private static bool _gaveUp; private static bool _hooked; private static bool _menuArmed; private static bool _dumped; private static float _nextAttempt; private static float _nextHoursRefresh; private static int _attempts; private static bool _shopStocked; private static float _nextShopAttempt; private static int _shopAttempts; private static NPC _dan; private static DialogueController _controller; private static readonly Dictionary<string, bool> _introChoices = new Dictionary<string, bool>(StringComparer.Ordinal); private static DialogueChoice _reaskChoice; private static DialogueContainer _reaskConvo; private static DialogueNodeData _reaskReplyNode; private static DialogueContainer _menuConvo; private static DialogueNodeData _menuEntryNode; private static DialogueNodeData _menuSoldNode; private static string _menuDishId; private static string[] _menuEntryLines; private static readonly string[] ClosedLines = new string[3] { "Store's closed. The lunch keeps till tomorrow.", "We're locked up. The crockpot's off.", "Closed for the night. Lunch resumes at eight." }; private static readonly string[] ChiliEntryLines = new string[3] { "Pot of my chili's going in the back. Eleven bucks a bowl.", "Chili's been on since morning. Eleven and it's yours.", "Hungry? The back-room chili's ready. Eleven a bowl." }; private static readonly string[] LasagnaEntryLines = new string[3] { "For my favorite comedian? I brought in the pan lasagna. Sixteen and it's yours.", "Made the pan lasagna last night. Comedians eat for sixteen.", "Got the lasagna in the back. Sixteen. Don't go telling everyone." }; private static readonly string[] ChiliSoldLines = new string[3] { "There you go. Careful, it's got a kick.", "One bowl. The secret's cumin. Don't tell my wife I told you.", "Fresh ladle. Spoon's in the bag." }; private static readonly string[] LasagnaSoldLines = new string[3] { "Corner piece. That's respect.", "There you go. The pan does the heavy lifting.", "Middle cut today. You earned it." }; private static readonly string[] ChiliBrokeLines = new string[3] { "Eleven bucks, chief. The pot doesn't run on goodwill.", "You're short. The chili keeps, come back.", "Eleven, chief. Even my wife pays for hers." }; private static readonly string[] LasagnaBrokeLines = new string[3] { "Sixteen, chief. Comedy doesn't cover it.", "You're short. The pan's not going anywhere.", "Sixteen bucks. I know you're good for it... just not today, huh." }; private static readonly string[] ChiliBoughtToasts = new string[3] { "One bowl of Dan's chili. Housemade.", "Dan's chili, secured.", "Chili from the hardware store. Sure. Why not." }; private static readonly string[] LasagnaBoughtToasts = new string[3] { "Dan's pan lasagna. He likes you.", "The comedian's cut. Still warm.", "Hardware-store lasagna. Genuinely touched." }; public static void OnSessionEnded() { _armed = false; _gaveUp = false; _hooked = false; _menuArmed = false; _dumped = false; _attempts = 0; _nextAttempt = 0f; _nextHoursRefresh = 0f; _shopStocked = false; _shopAttempts = 0; _nextShopAttempt = 0f; _dan = null; _controller = null; _introChoices.Clear(); _reaskChoice = null; _reaskConvo = null; _reaskReplyNode = null; _menuConvo = null; _menuEntryNode = null; _menuSoldNode = null; _menuDishId = null; _menuEntryLines = null; } private static int EffectiveAnswer() { string text = (NeedsConfig.DanDishMode ?? "auto").Trim().ToLowerInvariant(); if (text == "chili") { return 1; } if (text == "lasagna") { return 2; } return SaveData.DanAnswer; } public static void Tick() { if (!NeedsConfig.VendorDanDish) { return; } TickShop(); if (_gaveUp) { return; } if (_armed) { TickArmed(); return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextAttempt) { return; } _nextAttempt = unscaledTime + 2f; try { TryArm(); } catch (Exception ex) { _gaveUp = true; Diag.Log("[DanDish] wiring failed (" + ex.Message + ") — Dan's lunch unavailable this session."); } } private static void TickArmed() { if (!_menuArmed && EffectiveAnswer() != 0) { try { ArmMenu(); } catch (Exception ex) { Diag.Log("[DanDish] menu arm failed: " + ex.Message); _gaveUp = true; } } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextHoursRefresh) { return; } _nextHoursRefresh = unscaledTime + 5f; if (_menuEntryNode == null) { return; } try { _menuEntryNode.DialogueText = (EateryVendors.IsOpenNow(NeedsConfig.HoursHardware) ? Lines.Pick(_menuEntryLines) : Lines.Pick(ClosedLines)); } catch { } } private static void TickShop() { //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown if (_shopStocked) { return; } int num = EffectiveAnswer(); if (num == 0) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextShopAttempt) { return; } _nextShopAttempt = unscaledTime + 5f; string text = ((num == 2) ? "danlasagna" : "danchili"); try { if (!EateryVendors.TryGetDef(text, out var def) || (Object)(object)def == (Object)null) { return; } try { def.BasePurchasePrice = Mathf.Max(1f, (num == 2) ? NeedsConfig.DanLasagnaPrice : NeedsConfig.DanChiliPrice); } catch { } List<ShopInterface> allShops = ShopInterface.AllShops; if (allShops == null) { return; } int num2 = 0; int num3 = 0; for (int i = 0; i < allShops.Count; i++) { ShopInterface val = allShops[i]; if ((Object)(object)val == (Object)null || val.Listings == null) { continue; } bool flag = false; bool flag2 = false; ShopListing val2 = null; for (int j = 0; j < val.Listings.Count; j++) { ShopListing val3 = val.Listings[j]; if (val3 != null && !((Object)(object)val3.Item == (Object)null)) { string obj2 = ((BaseItemDefinition)val3.Item).ID ?? ""; if (obj2 == "soil") { flag = true; val2 = val3; } if (string.Equals(obj2, text, StringComparison.OrdinalIgnoreCase)) { flag2 = true; } } } if (!flag) { continue; } if (flag2) { num3++; continue; } ShopListing val4 = new ShopListing(); val4.name = ((BaseItemDefinition)def).Name; val4.Item = def; val4.LimitedStock = false; val4.RestockRate = (ERestockRate)0; val4.DefaultStock = 99; val4.CanBeDelivered = val2 != null && val2.CanBeDelivered; val.Listings.Add(val4); val4.Initialize(val); try { val.CreateListingUI(val4); } catch { } num2++; } if (num2 + num3 > 0) { _shopStocked = true; Diag.Log($"[DanDish] '{text}' stocked in the hardware store's NATIVE shop UI ({num2} added, {num3} already there) — the earned dish is on the shelf as well as in Dan's dialogue."); } else if (++_shopAttempts >= 24) { _shopStocked = true; Diag.Log("[DanDish] no soil-selling shop found — dish not stocked in the native shop UI this session (dialogue path unaffected)."); } } catch (Exception ex) { MelonLogger.Warning("[DanDish] shop stocking failed: " + ex.Message); } } private static void TryArm() { NPC val = null; try { val = NPCManager.GetNPC("dan_samwell"); if ((Object)(object)val == (Object)null) { Il2CppArrayBase<NPC> val2 = Object.FindObjectsOfType<NPC>(); for (int i = 0; i < val2.Length; i++) { NPC val3 = val2[i]; if (!((Object)(object)val3 == (Object)null)) { string text = ""; try { text = (val3.ID ?? "").ToLowerInvariant(); } catch { } if (text == "dan_samwell") { val = val3; break; } } } } } catch { } if ((Object)(object)val == (Object)null) { if (++_attempts >= 15) { _gaveUp = true; Diag.Log("[DanDish] NPC 'dan_samwell' not present in this world — feature skipped (not faked)."); } return; } DialogueController val4 = ((Component)val).GetComponent<DialogueController>() ?? ((Component)val).GetComponentInChildren<DialogueController>(true); if ((Object)(object)val4 == (Object)null && (Object)(object)val.DialogueHandler != (Object)null) { val4 = ((Component)val.DialogueHandler).GetComponent<DialogueController>() ?? ((Component)val.DialogueHandler).GetComponentInChildren<DialogueController>(true); } if ((Object)(object)val4 == (Object)null || val4.Choices == null) { _gaveUp = true; Diag.Log("[DanDish] Dan has no DialogueController — feature skipped."); return; } _dan = val; _controller = val4; DumpDanDialogue(); ScanIntroChoices(); DialogueHandler dialogueHandler = val.DialogueHandler; if ((Object)(object)dialogueHandler != (Object)null && !_hooked) { dialogueHandler.onDialogueChoiceChosen.AddListener(UnityAction<string>.op_Implicit((Action<string>)OnChoiceChosen)); _hooked = true; } if ((NeedsConfig.DanDishMode ?? "auto").Trim().ToLowerInvariant() == "auto" && SaveData.DanAnswer == 0) { ArmReask(); } if (EffectiveAnswer() != 0) { ArmMenu(); } _armed = true; Diag.Log($"[DanDish] armed on 'dan_samwell': answer={SaveData.DanAnswer} (0=unknown/1=denied/2=admitted), mode={NeedsConfig.DanDishMode}, introChoicesMapped={_introChoices.Count}, reask={_reaskChoice != null}, menu={_menuArmed}."); } private static void DumpDanDialogue() { if (_dumped || !NeedsConfig.VerboseConsume) { _dumped = true; return; } _dumped = true; try { Diag.Log("[DanDish] ===== DAN'S DIALOGUE ====="); foreach (DialogueContainer item in EnumerateDanContainers()) { DumpContainer(item); } Diag.Log("[DanDish] ===== END DAN'S DIALOGUE ====="); } catch (Exception ex) { Diag.Log("[DanDish] dialogue dump failed: " + ex.Message); } } private static IEnumerable<DialogueContainer> EnumerateDanContainers() { HashSet<IntPtr> seen = new HashSet<IntPtr>(); List<DialogueContainer> list = new List<DialogueContainer>(); try { Add(_controller.GenericDialogue); } catch { } try { if (_controller.Choices != null) { for (int i = 0; i < _controller.Choices.Count; i++) { if (_controller.Choices[i] != null) { Add(_controller.Choices[i].Conversation); } } } } catch { } try { DialogueHandler val = (((Object)(object)_dan != (Object)null) ? _dan.DialogueHandler : null); List<DialogueContainer> val2 = (((Object)(object)val != (Object)null) ? val.dialogueContainers : null); if (val2 != null) { for (int j = 0; j < val2.Count; j++) { Add(val2[j]); } } } catch { } return list; void Add(DialogueContainer c) { if ((Object)(object)c == (Object)null) { return; } try { if (seen.Add(((Il2CppObjectBase)c).Pointer)) { list.Add(c); } } catch { } } } private static void DumpContainer(DialogueContainer c) { try { if ((Object)(object)c == (Object)null || c.DialogueNodeData == null) { return; } Diag.Log($"[DanDish] container '{((Object)c).name}': {c.DialogueNodeData.Count} node(s), {((c.NodeLinks != null) ? c.NodeLinks.Count : 0)} link(s)"); Il2CppArrayBase<DialogueNodeData> val = c.DialogueNodeData.ToArray(); for (int i = 0; i < val.Length; i++) { DialogueNodeData val2 = val[i]; if (val2 == null) { continue; } int num = ((val2.choices != null) ? ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices).Length : 0); Diag.Log($"[DanDish] node[{i}] label='{val2.DialogueNodeLabel}' text='{Trunc(val2.DialogueText)}' choices={num}"); for (int j = 0; j < num; j++) { DialogueChoiceData val3 = ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[j]; if (val3 != null) { Diag.Log($"[DanDish] choice '{Trunc(val3.ChoiceText)}' label='{val3.ChoiceLabel}' guid='{val3.Guid}'"); } } } } catch (Exception ex) { Diag.Log("[DanDish] container dump failed: " + ex.Message); } } private static void ScanIntroChoices() { try { foreach (DialogueContainer item in EnumerateDanContainers()) { if ((Object)(object)item == (Object)null || item.DialogueNodeData == null) { continue; } Il2CppArrayBase<DialogueNodeData> val = item.DialogueNodeData.ToArray(); for (int i = 0; i < val.Length; i++) { DialogueNodeData val2 = val[i]; if (val2 == null || val2.choices == null || ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices).Length < 2) { continue; } bool flag = false; for (int j = 0; j < ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices).Length; j++) { DialogueChoiceData val3 = ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[j]; if (((val3 != null) ? (val3.ChoiceText ?? "").ToLowerInvariant() : "").Contains("drug")) { flag = true; break; } } if (!flag) { continue; } for (int k = 0; k < ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices).Length; k++) { DialogueChoiceData val4 = ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[k]; if (val4 != null) { bool value = (val4.ChoiceText ?? "").ToLowerInvariant().Contains("drug"); if (!string.IsNullOrEmpty(val4.ChoiceLabel)) { _introChoices[val4.ChoiceLabel] = value; } if (!string.IsNullOrEmpty(val4.Guid)) { _introChoices[val4.Guid] = value; } } } Diag.Log($"[DanDish] intro question found in '{((Object)item).name}' node[{i}] ('{Trunc(val2.DialogueText)}') — {((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices).Length} answer(s) mapped."); } } if (_introChoices.Count == 0) { Diag.Log("[DanDish] no intro question node present on Dan (already played out, or labels stripped) — the re-ask covers it."); } } catch (Exception ex) { Diag.Log("[DanDish] intro scan failed: " + ex.Message); } } private static void ArmReask() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Expected O, but got Unknown if (_reaskChoice == null) { DialogueContainer val = ScriptableObject.CreateInstance<DialogueContainer>(); ((Object)val).name = "S1HT_DanReask"; ((Object)val).hideFlags = (HideFlags)32; val.DialogueNodeData = new List<DialogueNodeData>(); val.NodeLinks = new List<NodeLinkData>(); val.BranchNodeData = new List<BranchNodeData>(); DialogueNodeData val2 = new DialogueNodeData(); val2.Guid = "S1HT_DAN_REASK_ENTRY"; val2.DialogueNodeLabel = TacoDiag.DiscoveredEntryLabel; val2.DialogueText = "Yeah? What about it?"; val2.choices = new Il2CppReferenceArray<DialogueChoiceData>(3L); DialogueChoiceData val3 = new DialogueChoiceData(); val3.Guid = "S1HT_DAN_ADMIT"; val3.ChoiceLabel = "S1HT_DAN_ADMIT"; val3.ChoiceText = "Actually... I deal drugs."; DialogueChoiceData val4 = new DialogueChoiceData(); val4.Guid = "S1HT_DAN_DENY"; val4.ChoiceLabel = "S1HT_DAN_DENY"; val4.ChoiceText = "Tomatoes. Strictly tomatoes."; DialogueChoiceData val5 = new DialogueChoiceData(); val5.Guid = "S1HT_DAN_NVM"; val5.ChoiceLabel = "S1HT_DAN_NVM"; val5.ChoiceText = "Never mind."; ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[0] = val3; ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[1] = val4; ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[2] = val5; DialogueNodeData val6 = new DialogueNodeData(); val6.Guid = "S1HT_DAN_REASK_REPLY"; val6.DialogueNodeLabel = val6.Guid; val6.DialogueText = "Alright then."; val6.choices = new Il2CppReferenceArray<DialogueChoiceData>(0L); val.DialogueNodeData.Add(val2); val.DialogueNodeData.Add(val6); string[] array = new string[3] { "S1HT_DAN_ADMIT", "S1HT_DAN_DENY", "S1HT_DAN_NVM" }; foreach (string baseChoiceOrOptionGUID in array) { NodeLinkData val7 = new NodeLinkData(); val7.BaseDialogueOrBranchNodeGuid = val2.Guid; val7.BaseChoiceOrOptionGUID = baseChoiceOrOptionGUID; val7.TargetNodeGuid = val6.Guid; val.NodeLinks.Add(val7); } _reaskConvo = val; _reaskReplyNode = val6; DialogueChoice val8 = new DialogueChoice(); val8.Enabled = true; val8.ChoiceText = "About what I'm growing..."; val8.ShowWorldspaceDialogue = false; val8.Conversation = val; val8.Priority = 0; val8.onChoosen = new UnityEvent(); try { _controller.AddDialogueChoice(val8, 0); } catch { _controller.Choices.Add(val8); } _reaskChoice = val8; RegisterContainer(val); Diag.Log("[DanDish] re-ask choice armed (this save predates the flag — the player tells Dan again)."); } } private static void CloseReask() { try { if (_reaskChoice != null) { _reaskChoice.Enabled = false; } } catch { } } private static void ArmMenu() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Expected O, but got Unknown //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Expected O, but got Unknown bool flag = EffectiveAnswer() == 2; _menuDishId = (flag ? "danlasagna" : "danchili"); if (!EateryVendors.TryGetDef(_menuDishId, out var _)) { Diag.Log("[DanDish] '" + _menuDishId + "' not registered yet — menu waits."); return; } _menuEntryLines = (flag ? LasagnaEntryLines : ChiliEntryLines); DialogueContainer val = ScriptableObject.CreateInstance<DialogueContainer>(); ((Object)val).name = "S1HT_DanLunch"; ((Object)val).hideFlags = (HideFlags)32; val.DialogueNodeData = new List<DialogueNodeData>(); val.NodeLinks = new List<NodeLinkData>(); val.BranchNodeData = new List<BranchNodeData>(); DialogueNodeData val2 = new DialogueNodeData(); val2.Guid = "S1HT_DAN_LUNCH_ENTRY"; val2.DialogueNodeLabel = TacoDiag.DiscoveredEntryLabel; val2.DialogueText = (EateryVendors.IsOpenNow(NeedsConfig.HoursHardware) ? Lines.Pick(_menuEntryLines) : Lines.Pick(ClosedLines)); val2.choices = new Il2CppReferenceArray<DialogueChoiceData>(2L); float value = (flag ? NeedsConfig.DanLasagnaPrice : NeedsConfig.DanChiliPrice); DialogueChoiceData val3 = new DialogueChoiceData(); val3.Guid = "S1HT_DAN_BUY"; val3.ChoiceLabel = "S1HT_DAN_BUY"; val3.ChoiceText = (flag ? $"Dan's Homemade Pan Lasagna (${value:0})" : $"Dan's Homemade Chili (${value:0})"); DialogueChoiceData val4 = new DialogueChoiceData(); val4.Guid = "S1HT_DAN_BYE"; val4.ChoiceLabel = "S1HT_DAN_BYE"; val4.ChoiceText = "Nah, all good."; ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[0] = val3; ((Il2CppArrayBase<DialogueChoiceData>)(object)val2.choices)[1] = val4; DialogueNodeData val5 = new DialogueNodeData(); val5.Guid = "S1HT_DAN_LUNCH_SOLD"; val5.DialogueNodeLabel = val5.Guid; val5.DialogueText = "There you go."; val5.choices = new Il2CppReferenceArray<DialogueChoiceData>(0L); DialogueNodeData val6 = new DialogueNodeData(); val6.Guid = "S1HT_DAN_LUNCH_BYE"; val6.DialogueNodeLabel = val6.Guid; val6.DialogueText = "Suit yourself."; val6.choices = new Il2CppReferenceArray<DialogueChoiceData>(0L); val.DialogueNodeData.Add(val2); val.DialogueNodeData.Add(val5); val.DialogueNodeData.Add(val6); NodeLinkData val7 = new NodeLinkData(); val7.BaseDialogueOrBranchNodeGuid = val2.Guid; val7.BaseChoiceOrOptionGUID = "S1HT_DAN_BUY"; val7.TargetNodeGuid = val5.Guid; val.NodeLinks.Add(val7); NodeLinkData val8 = new NodeLinkData(); val8.BaseDialogueOrBranchNodeGuid = val2.Guid; val8.BaseChoiceOrOptionGUID = "S1HT_DAN_BYE"; val8.TargetNodeGuid = val6.Guid; val.NodeLinks.Add(val8); DialogueChoice val9 = new DialogueChoice(); val9.Enabled = true; val9.ChoiceText = "Ask about lunch"; val9.ShowWorldspaceDialogue = false; val9.Conversation = val; val9.Priority = 0; val9.onChoosen = new UnityEvent(); try { _controller.AddDialogueChoice(val9, 0); } catch { _controller.Choices.Add(val9); } _menuConvo = val; _menuEntryNode = val2; _menuSoldNode = val5; RegisterContainer(val); _menuArmed = true; Diag.Log($"[DanDish] lunch menu armed: '{_menuDishId}' at ${value:0} ({(flag ? "admitted → lasagna" : "denied → chili")})."); } private static void RegisterContainer(DialogueContainer c) { try { DialogueHandler val = (((Object)(object)_dan != (Object)null) ? _dan.DialogueHandler : null); List<DialogueContainer> val2 = (((Object)(object)val != (Object)null) ? val.dialogueContainers : null); if (val2 != null && !val2.Contains(c)) { val2.Add(c); } } catch { } } private static void OnChoiceChosen(string chosen) { try { if (string.IsNullOrEmpty(chosen)) { return; } if (NeedsConfig.VerboseConsume && !chosen.StartsWith("S1HT_")) { Diag.Log("[DanDish] Dan dialogue choice fired: '" + chosen + "'."); } switch (chosen) { case "S1HT_DAN_ADMIT": SetAnswer(2, "re-ask"); return; case "S1HT_DAN_DENY": SetAnswer(1, "re-ask"); return; case "S1HT_DAN_BUY": Buy(); return; } if (_introChoices.TryGetValue(chosen, out var value) && SaveData.DanAnswer == 0) { SetAnswer((!value) ? 1 : 2, "intro dialogue"); } } catch (Exception ex) { Diag.Log("[DanDish] choice handling failed: " + ex.Message); } } private static void SetAnswer(int value, string source) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) SaveData.DanAnswer = value; CloseReask(); if (_reaskReplyNode != null && source == "re-ask") { _reaskReplyNode.DialogueText = ((value == 2) ? Lines.Pick(new string[2] { "HA. Still committed to the bit. Alright, comedian. Ask me about lunch sometime.", "Ha. Same joke, still funny. Ask me about lunch sometime, comedian." }) : Lines.Pick(new string[2] { "Tomatoes, right. Well, everybody's gotta eat. Ask me about lunch sometime.", "Tomatoes. Good crop this year, I hear. Ask me about lunch sometime." })); } NeedsHud.Toast((value == 2) ? "Dan thinks you're hilarious. Noted." : "Tomatoes it is.", NeedsHud.ToastNeutral); Diag.Log($"[DanDish] answer recorded via {source}: {((value == 2) ? "ADMITTED → lasagna" : "DENIED → chili")} (persists on next game save)."); } private static void Buy() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) try { bool flag = EffectiveAnswer() == 2; string text = _menuDishId ?? (flag ? "danlasagna" : "danchili"); if (!EateryVendors.TryGetDef(text, out var def) || (Object)(object)def == (Object)null) { Diag.Log("[DanDish] '" + text + "' has no definition — sale aborted."); return; } if (!EateryVendors.IsOpenNow(NeedsConfig.HoursHardware)) { if (_menuSoldNode != null) { _menuSoldNode.DialogueText = Lines.Pick(ClosedLines); } NeedsHud.Toast("Shut. Come back when they're open.", NeedsHud.ToastNeutral); Diag.Log("[DanDish] sale blocked — closed (hours " + NeedsConfig.HoursHardware + ")."); return; } float num = (flag ? NeedsConfig.DanLasagnaPrice : NeedsConfig.DanChiliPrice); MoneyManager instance = NetworkSingleton<MoneyManager>.Instance; PlayerInventory instance2 = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return; } if (instance.cashBalance < num) { if (_menuSoldNode != null) { _menuSoldNode.DialogueText = (flag ? Lines.Pick(LasagnaBrokeLines) : Lines.Pick(ChiliBrokeLines)); } NeedsHud.Toast($"That's ${num:0} cash. You haven't got it.", NeedsHud.ToastNeutral); return; } instance.ChangeCashBalance(0f - num, true, true); if (_menuSoldNode != null) { _menuSoldNode.DialogueText = (flag ? Lines.Pick(LasagnaSoldLines) : Lines.Pick(ChiliSoldLines)); } instance2.AddItemToInventory(((ItemDefinition)def).GetDefaultInstance(1)); NeedsHud.Toast(flag ? Lines.Pick(LasagnaBoughtToasts) : Lines.Pick(ChiliBoughtToasts), NeedsHud.ToastGood); Diag.Log($"[DanDish] '{text}' sold for ${num:0}."); } catch (Exception ex) { Diag.Log("[DanDish] purchase failed: " + ex.Message); } } private static string Trunc(string s) { if (!string.IsNullOrEmpty(s)) { if (s.Length <= 48) { return s; } return s.Substring(0, 48) + "…"; } return ""; } } public static class Diag { private static readonly object _lock = new object(); private static bool _headerWritten; private static string PathFile => Path.Combine(MelonEnvironment.UserDataDirectory, "S1HungerThirst", "diag.txt"); public static void Log(string message) { try { MelonLogger.Msg(message); } catch { } try { lock (_lock) { string pathFile = PathFile; Directory.CreateDirectory(Path.GetDirectoryName(pathFile)); if (!_headerWritten) { _headerWritten = true; try { FileInfo fileInfo = new FileInfo(pathFile); if (fileInfo.Exists && fileInfo.Length > 262144) { File.Delete(pathFile); } } catch { } File.AppendAllText(pathFile, $"{Environment.NewLine}===== session {DateTime.Now:yyyy-MM-dd HH:mm:ss} ====={Environment.NewLine}"); } File.AppendAllText(pathFile, $"[{DateTime.Now:HH:mm:ss.fff}] {message}{Environment.NewLine}"); } } catch { } } } public static class DrugEffects { private static readonly string[] CrashLines = new string[3] { "There's the come-down. Wonderful.", "And there it goes. The crash tax.", "The loan came due. Paid in energy." }; private static readonly string[] ComedownLines = new string[3] { "Coming down. Appetite's back to normal.", "Levelling out. Back to plain old hungry.", "That's the high gone. Business as usual." }; private static readonly Dictionary<string, Restore> _effectRates = new Dictionary<string, Restore>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Restore> _drugTypeRates = new Dictionary<string, Restore>(StringComparer.OrdinalIgnoreCase); private static IntPtr _lastProductPtr = IntPtr.Zero; private static bool _munchiesActive; public static float HungerMult { get; private set; } = 1f; public static float ThirstMult { get; private set; } = 1f; public static float EnergyMult { get; private set; } = 1f; public static bool HungerFaster => HungerMult > 1.15f; public static bool HungerSlower => HungerMult < 0.85f; public static bool ThirstFaster => ThirstMult > 1.15f; public static bool ThirstSlower => ThirstMult < 0.85f; public static bool EnergyFaster => EnergyMult > 1.15f; public static bool EnergySlower => EnergyMult < 0.85f; public static bool StrongStimActive => EnergyMult <= NeedsConfig.StimStrongThreshold; public static void ParseConfigMaps() { ParseInto(_effectRates, NeedsConfig.EffectRatesRaw); ParseInto(_drugTypeRates, NeedsConfig.DrugTypeRatesRaw); _lastProductPtr = IntPtr.Zero; } private static void ParseInto(Dictionary<string, Restore> map, string raw) { map.Clear(); string[] array = (raw ?? "").Split(';', StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('=', 2); if (array2.Length == 2) { string[] array3 = array2[1].Split('/', 3); float hunger = NeedsConfig.ParseF(array3[0].Trim(), 1f); float thirst = ((array3.Length > 1) ? NeedsConfig.ParseF(array3[1].Trim(), 1f) : 1f); float energy = ((array3.Length > 2) ? NeedsConfig.ParseF(array3[2].Trim(), 1f) : 1f); map[Normalize(array2[0])] = new Restore(hunger, thirst, energy); } } } private static string Normalize(string s) { return Regex.Replace(s ?? "", "[^A-Za-z0-9]", "").ToLowerInvariant(); } public static void Reset() { HungerMult = (ThirstMult = (EnergyMult = 1f)); _lastProductPtr = IntPtr.Zero; _munchiesActive = false; } public static void Tick() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) if (!NeedsConfig.DrugEffectsEnabled) { if (HungerMult != 1f || ThirstMult != 1f) { Reset(); } return; } Player local = Player.Local; ProductItemInstance val = (((Object)(object)local != (Object)null) ? local.ConsumedProduct : null); if (val == null) { if (_lastProductPtr != IntPtr.Zero) { bool num = NeedsConfig.EnergyEnabled && NeedsConfig.StimCrashEnabled && StrongStimActive; Reset(); if (num) { NeedsState.DrainEnergy(NeedsConfig.StimCrashCost); NeedsHud.Toast(Lines.Pick(CrashLines), NeedsHud.ToastBad); } else { NeedsHud.Toast(Lines.Pick(ComedownLines), NeedsHud.ToastNeutral); } } } else { if (((Il2CppObjectBase)val).Pointer == _lastProductPtr) { return; } _lastProductPtr = ((Il2CppObjectBase)val).Pointer; float num2 = 0f; float num3 = 0f; float num4 = 0f; bool munchiesActive = false; List<string> list = new List<string>(6); try { ProductDefinition val2 = (((Object)(object)((ItemInstance)val).Definition != (Object)null) ? ((Il2CppObjectBase)((ItemInstance)val).Definition).TryCast<ProductDefinition>() : null); if ((Object)(object)val2 != (Object)null) { if (!(((BaseItemDefinition)val2).ID ?? "").StartsWith("s1ht_fx_", StringComparison.OrdinalIgnoreCase) && _drugTypeRates.TryGetValue(Normalize(((object)val2.DrugType/*cast due to .constrained prefix*/).ToString()), out var value)) { num2 += value.Hunger - 1f; num3 += value.Thirst - 1f; num4 += value.Energy - 1f; string text = Phrase(value); if (text != null) { list.Add($"{val2.DrugType}... {text}"); } } Il2CppArrayBase<Effect> val3 = ((((PropertyItemDefinition)val2).Properties != null) ? ((PropertyItemDefinition)val2).Properties.ToArray() : null); if (val3 != null) { for (int i = 0; i < val3.Length; i++) { Effect val4 = val3[i]; if ((Object)(object)val4 == (Object)null || !TryMatch(val4, out var rate)) { continue; } num2 += rate.Hunger - 1f; num3 += rate.Thirst - 1f; num4 += rate.Energy - 1f; int num5; if (!(Normalize(((MemberInfo)((Object)val4).GetIl2CppType()).Name) == "munchies")) { num5 = ((Normalize(val4.ID) == "munchies") ? 1 : 0); if (num5 == 0) { goto IL_0289; } } else { num5 = 1; } munchiesActive = true; goto IL_0289; IL_0289: string text2 = ((num5 != 0) ? "munchies just kicked in. Hard." : Phrase(rate)); if (text2 != null) { string text3 = ((!string.IsNullOrEmpty(val4.Name)) ? val4.Name : ((MemberInfo)((Object)val4).GetIl2CppType()).Name); list.Add(text3 + "... " + text2); } } } } } catch (Exception ex) { MelonLogger.Warning("[Drugs] Could not read product effects: " + ex.Message); } HungerMult = Mathf.Clamp(1f + num2, NeedsConfig.DrugMinMult, NeedsConfig.DrugMaxMult); ThirstMult = Mathf.Clamp(1f + num3, NeedsConfig.DrugMinMult, NeedsConfig.DrugMaxMult); EnergyMult = (NeedsConfig.EnergyEnabled ? Mathf.Clamp(1f + num4, 0.1f, NeedsConfig.DrugMaxMult) : 1f); _munchiesActive = munchiesActive; if (NeedsConfig.EnergyEnabled && NeedsConfig.StimKickEnergy > 0f) { if (EnergyMult <= NeedsConfig.StimStrongThreshold) { NeedsState.Replenish(0f, 0f, NeedsConfig.StimKickEnergy); list.Add("Good gear. Wide awake now."); } else if (EnergyMult < NeedsConfig.StimMildThreshold) { NeedsState.Replenish(0f, 0f, NeedsConfig.StimKickEnergy * NeedsConfig.StimKickMildFactor); list.Add("Bit of a buzz."); } } int num6 = 0; foreach (string item in list) { if (num6++ >= 5) { NeedsHud.Toast("...plus a few more", NeedsHud.ToastWarn); break; } NeedsHud.Toast(item, NeedsHud.ToastWarn); } } } private static string Phrase(Restore r) { float num = r.Hunger - 1f; float num2 = r.Thirst - 1f; float num3 = (NeedsConfig.EnergyEnabled ? (r.Energy - 1f) : 0f); float num4 = Mathf.Abs(num); float num5 = Mathf.Abs(num2); float num6 = Mathf.Abs(num3); if (num4 < 0.1f && num5 < 0.1f && num6 < 0.1f) { return null; } if (num4 >= num5 && num4 >= num6) { if (!(num > 0f)) { return "not hungry at all now"; } return "making you hungry, fast"; } if (num5 >= num6) { if (!(num2 > 0f)) { return "keeping the thirst away"; } return "drying you right out"; } if (!(num3 > 0f)) { return "keeping you wide awake"; } return "making you sleepy"; } private static bool TryMatch(Effect fx, out Restore rate) { if (_effectRates.TryGetValue(Normalize(((MemberInfo)((Object)fx).GetIl2CppType()).Name), out rate)) { return true; } if (_effectRates.TryGetValue(Normalize(fx.ID), out rate)) { return true; } if (_effectRates.TryGetValue(Normalize(fx.Name), out rate)) { return true; } return false; } } public static class EateryVendors { internal enum MeshKind { Roll, Cup, Folded, Slab, Native, Bowl, BruleeBowl, Mug, GlassTall, GlassShort, Bottle, Can, NoodleCup, StrawCup, Croissant, PastryDisc, Sandwich, Slice, Bun, FilledBun, Hotdog, TakeoutBox, Carton, Brownie, PlateSteak, Chicken, Fish, Tray } private sealed class Item { public string Id; public string Name; public string Desc; public string[] SoldLines; public float Price; public MeshKind Kind; public Taco3D.FillingLook Look; } private sealed class Vendor { public string Key; public string Establishment; public string NpcId; public string FirstName; public string MenuChoice; public string[] EntryLines; public string[] ByeLines; public string[] BrokeLines; public string[] ClosedLines; public Item[] Items; public Func<bool> Enabled; public Func<string> Hours; public bool Armed; public bool GaveUp; public bool Hooked; public float NextAttempt; public int Attempts; public DialogueContainer Convo; public DialogueNodeData SoldNode; public DialogueNodeData EntryNode; public DialogueNodeData ByeNode; } private static readonly Vendor[] Vendors; private static readonly string[] GasMartItems; private static readonly Item[] ShopOnlyItems; private static readonly Item[] DanDishItems; private const string BuyPrefix = "S1HT_EAT_BUY_"; private static readonly Dictionary<string, StorableItemDefinition> _defs; private static readonly Dictionary<string, Item> _items; private static bool _gasMartStocked; private static float _nextGasMartAttempt; private static int _gasMartAttempts; private static float _nextHoursRefresh; private static readonly string[] PaidToasts; private static readonly string[] FreeToasts; pr