Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Talented v1.0.4
Talented.dll
Decompiled 5 days 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.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; 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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Talented")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4+c6a981afe69f1be9149ea5f0a8216e120b61c091")] [assembly: AssemblyProduct("Talented")] [assembly: AssemblyTitle("Talented")] [assembly: AssemblyVersion("1.0.4.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 DwarfHavenGames.Talented { internal static class BonusDrops { private static readonly FieldRef<Character, HitData> LastHit = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit"); private static readonly FieldRef<Character, int> CharacterLevel = AccessTools.FieldRefAccess<Character, int>("m_level"); internal static readonly HashSet<SkillType> Combat = new HashSet<SkillType> { (SkillType)1, (SkillType)2, (SkillType)3, (SkillType)4, (SkillType)5, (SkillType)7, (SkillType)11, (SkillType)8, (SkillType)14, (SkillType)9, (SkillType)10 }; private static bool s_rolling; internal static void Append(CharacterDrop drop, List<KeyValuePair<GameObject, int>> result) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) if (s_rolling || !TalentedPlugin.Settings.BonusDropsEnabled.Value) { return; } Character component = ((Component)drop).GetComponent<Character>(); if ((Object)(object)component == (Object)null || (!TalentedPlugin.Settings.AllowBossDrops.Value && component.IsBoss())) { return; } HitData val = LastHit.Invoke(component); if (val == null || !Combat.Contains(val.m_skill)) { return; } float expected; float draw; int num = BonusRoll.Count(val.m_skillLevel, TalentedPlugin.Settings.MaxBonusRolls.Value, out expected, out draw); if (num <= 0) { LogRoll(component, val, expected, draw, 0, 0, 0, 0, null); return; } bool flag = BonusRoll.WasLucky(expected, draw); int num2 = 0; int num3 = 0; int num4 = CharacterLevel.Invoke(component); Dictionary<string, int> dictionary = new Dictionary<string, int>(); s_rolling = true; try { CharacterLevel.Invoke(component) = 1; for (int i = 0; i < num; i++) { int num5 = num2; foreach (KeyValuePair<GameObject, int> item in drop.GenerateDropList()) { if (Eligible(drop, item.Key)) { result.Add(item); num2 += item.Value; dictionary.TryGetValue(((Object)item.Key).name, out var value); dictionary[((Object)item.Key).name] = value + item.Value; } } if (flag && i == num - 1) { num3 = num2 - num5; } } } finally { CharacterLevel.Invoke(component) = num4; s_rolling = false; } if (num3 > 0) { BonusEffect.Play(component.GetCenterPoint()); } LogRoll(component, val, expected, draw, num, num2, num3, num4, dictionary); } internal static void StampSkillLevel(HitData hit) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (hit != null && !(hit.m_skillLevel > 0f) && (int)hit.m_skill != 0) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)hit.GetAttacker() != (Object)(object)localPlayer)) { hit.m_skillLevel = ((Character)localPlayer).GetSkillLevel(hit.m_skill); } } } private static void LogRoll(Character character, HitData hit, float expected, float draw, int rolls, int items, int luckyItems, int level, Dictionary<string, int> paid) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (TalentedPlugin.Settings.LogBonusRolls.Value) { int num = (int)expected; float num2 = expected - (float)num; TalentedPlugin.Log.LogInfo((object)($"{((Object)character).name}: {hit.m_skill} {hit.m_skillLevel:0.#} -> {expected:0.000} rolls " + $"({num} + {num2:0.000} into the interval = " + $"{BonusRoll.Chance(expected):0.000} chance, drew {draw:0.000}) " + $"-> {rolls} roll(s), {items} item(s)" + ((paid == null || paid.Count == 0) ? "" : (" [" + Describe(paid) + "]")) + $" [level {level}]" + ((luckyItems > 0) ? $", lucky roll paid {luckyItems} and sparked" : ""))); } } private static string Describe(Dictionary<string, int> paid) { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, int> item in paid) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(item.Key).Append(" x").Append(item.Value); } return stringBuilder.ToString(); } private static bool Eligible(CharacterDrop drop, GameObject prefab) { if ((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null) { return false; } if (TalentedPlugin.Settings.ExcludedCombatItems.Contains(((Object)prefab).name)) { return false; } foreach (Drop drop2 in drop.m_drops) { if ((Object)(object)drop2.m_prefab == (Object)(object)prefab && drop2.m_onePerPlayer) { return false; } } return true; } } internal static class BonusEffect { private const string BundleName = "talented"; private const string PrefabName = "vfx_Bonus_Roll"; private static AssetBundle s_bundle; private static GameObject s_prefab; private static float s_lifetime; private static EffectList s_fallback; internal static void Load() { try { s_bundle = LoadBundle(); s_prefab = (((Object)(object)s_bundle == (Object)null) ? null : s_bundle.LoadAsset<GameObject>("vfx_Bonus_Roll")); } catch (Exception ex) { TalentedPlugin.Log.LogWarning((object)("could not load the talented bundle: " + ex.Message)); } if ((Object)(object)s_prefab == (Object)null) { TalentedPlugin.Log.LogWarning((object)"vfx_Bonus_Roll not found in the talented bundle - falling back to vanilla's bonus-yield effect"); return; } Strip(s_prefab); s_lifetime = LifetimeOf(s_prefab); TalentedPlugin.Log.LogInfo((object)string.Format("bonus effect: {0}, cleaned up after {1:0.#}s", "vfx_Bonus_Roll", s_lifetime)); } private static AssetBundle LoadBundle() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = null; string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); foreach (string text2 in manifestResourceNames) { if (text2.EndsWith("talented", StringComparison.Ordinal)) { text = text2; break; } } if (text == null) { TalentedPlugin.Log.LogWarning((object)"no embedded resource named talented - was the bundle built before this DLL was compiled?"); return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } 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; } } return AssetBundle.LoadFromMemory(array); } private static void Strip(GameObject prefab) { ZSyncTransform[] componentsInChildren = prefab.GetComponentsInChildren<ZSyncTransform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i], true); } ZNetView[] componentsInChildren2 = prefab.GetComponentsInChildren<ZNetView>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i], true); } } private static float LifetimeOf(GameObject prefab) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) float num = 0f; ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { MainModule main = componentsInChildren[i].main; MinMaxCurve val = ((MainModule)(ref main)).startDelay; float num2 = ((MinMaxCurve)(ref val)).constantMax + (((MainModule)(ref main)).loop ? 0f : ((MainModule)(ref main)).duration); val = ((MainModule)(ref main)).startLifetime; float num3 = num2 + ((MinMaxCurve)(ref val)).constantMax; if (num3 > num) { num = num3; } } if (!(num <= 0f)) { return num + 1f; } return 5f; } internal static void Resolve(ZNetScene scene) { if ((Object)(object)scene == (Object)null || (Object)(object)s_prefab != (Object)null) { return; } foreach (GameObject prefab in scene.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Pickable component = prefab.GetComponent<Pickable>(); EffectData[] array = component?.m_bonusEffect?.m_effectPrefabs; if (array != null && array.Length != 0) { s_fallback = component.m_bonusEffect; if (TalentedPlugin.Settings.LogBonusRolls.Value) { TalentedPlugin.Log.LogInfo((object)("bonus effect taken from " + ((Object)prefab).name + ": " + Describe(array))); } return; } } TalentedPlugin.Log.LogWarning((object)"no bonus-yield effect found in this game's prefabs - lucky rolls will be invisible"); } internal static void Play(Vector3 pos) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_prefab != (Object)null) { Object.Destroy((Object)(object)Object.Instantiate<GameObject>(s_prefab, pos, Quaternion.identity), s_lifetime); return; } EffectList obj = s_fallback; GameObject[] array = ((obj != null) ? obj.Create(pos, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)) : null); if (array != null) { GameObject[] array2 = array; for (int i = 0; i < array2.Length; i++) { Silence(array2[i]); } } } private static void Silence(GameObject instance) { if (!((Object)(object)instance == (Object)null)) { ZSFX[] componentsInChildren = instance.GetComponentsInChildren<ZSFX>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } AudioSource[] componentsInChildren2 = instance.GetComponentsInChildren<AudioSource>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } } } private static string Describe(EffectData[] effects) { string text = ""; foreach (EffectData val in effects) { if (!((Object)(object)val?.m_prefab == (Object)null)) { text += ((text.Length == 0) ? ((Object)val.m_prefab).name : (", " + ((Object)val.m_prefab).name)); } } return text; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix(ZNetScene __instance) { BonusEffect.Resolve(__instance); } } internal static class BonusRoll { internal static int Count(float skillLevel, float maxRolls, out float expected, out float draw) { expected = Expected(skillLevel, maxRolls); draw = Random.value; return Whole(expected) + ((draw < Chance(expected)) ? 1 : 0); } internal static float Expected(float skillLevel, float maxRolls) { return Mathf.Clamp01(skillLevel / 100f) * maxRolls; } internal static int Whole(float expected) { return (int)expected; } internal static float Chance(float expected) { return Mathf.Clamp01((expected - (float)(int)expected - 0.5f) * 2f); } internal static bool WasLucky(float expected, float draw) { return draw < Chance(expected); } } internal static class BuildWear { internal const string SkillKey = "talented.craftingAtBuilding"; private static Player s_builder; internal static Player Builder => s_builder; internal static void BeginBuilding(Player player) { s_builder = (((Object)(object)player == (Object)(object)Player.m_localPlayer) ? player : null); } internal static void EndBuilding() { s_builder = null; } internal static void Apply(WearNTear wear) { ZNetView component = ((Component)wear).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null) { return; } ZDO zDO = component.GetZDO(); if (zDO != null) { if ((Object)(object)s_builder != (Object)null) { zDO.Set("talented.craftingAtBuilding", ((Character)s_builder).GetSkillLevel((SkillType)107)); } ApplyFlag(wear, zDO.GetFloat("talented.craftingAtBuilding", -1f)); } } internal static void Reinforce(WearNTear wear) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } ZNetView component = ((Component)wear).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } float skillLevel = ((Character)localPlayer).GetSkillLevel((SkillType)107); ZDO zDO = component.GetZDO(); if (zDO != null && !(skillLevel <= zDO.GetFloat("talented.craftingAtBuilding", -1f))) { component.ClaimOwnership(); zDO.Set("talented.craftingAtBuilding", skillLevel); ApplyFlag(wear, skillLevel); Fireplace component2 = ((Component)wear).GetComponent<Fireplace>(); if ((Object)(object)component2 != (Object)null) { FireFuel.Apply(component2); } } } private static void ApplyFlag(WearNTear wear, float built) { if (!(built < 0f) && TalentedPlugin.Settings.CraftingEnabled.Value && built >= TalentedPlugin.Settings.BuildWeatherWearStopsAt.Value) { wear.m_noRoofWear = false; } } } [HarmonyPatch(typeof(WearNTear), "Repair")] internal static class BuildWearRepairPatch { private static void Prefix(WearNTear __instance) { BuildWear.Reinforce(__instance); } } [HarmonyPatch(typeof(WearNTear), "Awake")] internal static class BuildWearAwakePatch { private static void Postfix(WearNTear __instance) { BuildWear.Apply(__instance); } } [HarmonyPatch(typeof(Player), "PlacePiece")] internal static class BuildWearPlacePatch { private static void Prefix(Player __instance) { BuildWear.BeginBuilding(__instance); } private static void Finalizer() { BuildWear.EndBuilding(); } } internal class Config { public ConfigEntry<bool> BonusDropsEnabled { get; } public ConfigEntry<float> MaxBonusRolls { get; } public ConfigEntry<bool> AllowBossDrops { get; } public NameList ExcludedCombatItems { get; } public ConfigEntry<bool> TreeDropsEnabled { get; } public ConfigEntry<float> TrunkMaxBonusRolls { get; } public ConfigEntry<float> LogMaxBonusRolls { get; } public NameList ExcludedTreeItems { get; } public ConfigEntry<bool> MiningDropsEnabled { get; } public ConfigEntry<float> MiningMaxBonusRolls { get; } public NameList MiningByProducts { get; } public NameList ExcludedMiningItems { get; } public ConfigEntry<bool> PlantingEnabled { get; } public ConfigEntry<float> PlantExtraStamina { get; } public ConfigEntry<float> PlantAnyBiomeAt { get; } public ConfigEntry<float> PlantSnapStartsAt { get; } public LevelList PlantRowUnlocks { get; } public LevelList PlantWidthUnlocks { get; } public ConfigEntry<KeyboardShortcut> CycleShapeModifier { get; } public ConfigEntry<bool> CookingEnabled { get; } public ConfigEntry<float> CookPlateauPercent { get; } public ConfigEntry<bool> CookStackFoods { get; } public ConfigEntry<bool> CraftingEnabled { get; } public ConfigEntry<float> BuildWeatherWearStopsAt { get; } public ConfigEntry<float> CraftQualityChance { get; } public ConfigEntry<float> StationCraftXp { get; } public ConfigEntry<bool> ForgeEnabled { get; } public ConfigEntry<float> UpgradeBreakImmunityAt { get; } public ConfigEntry<float> UpgradeSuccessBonus { get; } public ConfigEntry<float> FireNoFuelAt { get; } public ConfigEntry<string> FireAlwaysNeedsFuel { get; } public ConfigEntry<bool> PathfindingEnabled { get; } public ConfigEntry<float> PathSkillGainRate { get; } public ConfigEntry<bool> PathAnnounce { get; } public ConfigEntry<float> PathExploreRadiusBonus { get; } public ConfigEntry<float> PathSailBonus { get; } public ConfigEntry<float> PathDungeonMaxBonusRolls { get; } public ConfigEntry<float> PathDungeonStackPercent { get; } public NameList ExcludedDungeonItems { get; } public ConfigEntry<bool> ExemptNonCombatSkills { get; } public ConfigEntry<string> AlsoExemptSkills { get; } public ConfigEntry<bool> LogBonusRolls { get; } public ConfigEntry<bool> LogPlanting { get; } public ConfigEntry<bool> LogCooking { get; } public Config(ConfigFile config) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Expected O, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Expected O, but got Unknown //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Expected O, but got Unknown //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Expected O, but got Unknown //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Expected O, but got Unknown //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Expected O, but got Unknown //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Expected O, but got Unknown //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0475: Expected O, but got Unknown //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Expected O, but got Unknown //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Expected O, but got Unknown //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Expected O, but got Unknown //IL_054f: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Expected O, but got Unknown //IL_0591: Unknown result type (might be due to invalid IL or missing references) //IL_059b: Expected O, but got Unknown //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_05dd: Expected O, but got Unknown //IL_0607: Unknown result type (might be due to invalid IL or missing references) //IL_0611: Expected O, but got Unknown //IL_0649: Unknown result type (might be due to invalid IL or missing references) //IL_0653: Expected O, but got Unknown //IL_0679: Unknown result type (might be due to invalid IL or missing references) //IL_0683: Expected O, but got Unknown //IL_06bb: Unknown result type (might be due to invalid IL or missing references) //IL_06c5: Expected O, but got Unknown //IL_06fd: Unknown result type (might be due to invalid IL or missing references) //IL_0707: Expected O, but got Unknown //IL_072d: Unknown result type (might be due to invalid IL or missing references) //IL_0737: Expected O, but got Unknown //IL_076f: Unknown result type (might be due to invalid IL or missing references) //IL_0779: Expected O, but got Unknown //IL_079f: Unknown result type (might be due to invalid IL or missing references) //IL_07a9: Expected O, but got Unknown //IL_07e1: Unknown result type (might be due to invalid IL or missing references) //IL_07eb: Expected O, but got Unknown //IL_0823: Unknown result type (might be due to invalid IL or missing references) //IL_082d: Expected O, but got Unknown //IL_0865: Unknown result type (might be due to invalid IL or missing references) //IL_086f: Expected O, but got Unknown //IL_08a7: Unknown result type (might be due to invalid IL or missing references) //IL_08b1: Expected O, but got Unknown //IL_08e4: Unknown result type (might be due to invalid IL or missing references) //IL_08ee: Expected O, but got Unknown //IL_090b: Unknown result type (might be due to invalid IL or missing references) //IL_0915: Expected O, but got Unknown //IL_0932: Unknown result type (might be due to invalid IL or missing references) //IL_093c: Expected O, but got Unknown BonusDropsEnabled = config.Bind<bool>("01 - Combat Drops", "Extra Drops Per Kill", true, new ConfigDescription("Your weapon skill pays extra loot on a kill.", (AcceptableValueBase)null, new object[1] { Switch() })); MaxBonusRolls = config.Bind<float>("01 - Combat Drops", "Max Bonus Rolls", 4f, new ConfigDescription("How many bonus rolls of a creature's loot a kill pays at skill 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Primary() })); AllowBossDrops = config.Bind<bool>("01 - Combat Drops", "Allow Extra Boss Drops", false, new ConfigDescription("Bosses pay the bonus too.", (AcceptableValueBase)null, new object[1] { AdminOnly() })); ExcludedCombatItems = Exclusion(config, "01 - Combat Drops"); ExemptNonCombatSkills = config.Bind<bool>("02 - Death Penalty", "Exempt Non-Combat Skills", true, new ConfigDescription("Dying costs you no progress in the skills you do not fight with.", (AcceptableValueBase)null, new object[1] { Switch() })); AlsoExemptSkills = config.Bind<string>("02 - Death Penalty", "Also Exempt", "", new ConfigDescription("Extra skills a death should leave alone, comma separated. Run talented_skills in the console for the names.", (AcceptableValueBase)null, new object[1] { AdminOnly() })); TreeDropsEnabled = config.Bind<bool>("03 - Woodcutting Drops", "Extra Drops Per Tree", true, new ConfigDescription("Your woodcutting skill pays extra from a felled tree, from its logs and from the small trees and bushes you clear.", (AcceptableValueBase)null, new object[1] { Switch() })); TrunkMaxBonusRolls = config.Bind<float>("03 - Woodcutting Drops", "Trunk Max Bonus Rolls", 6f, new ConfigDescription("How many bonus rolls a felled trunk pays at skill 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Primary() })); LogMaxBonusRolls = config.Bind<float>("03 - Woodcutting Drops", "Log Max Bonus Rolls", 2f, new ConfigDescription("How many bonus rolls the logs and small trees pay at skill 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Primary() })); ExcludedTreeItems = Exclusion(config, "03 - Woodcutting Drops"); MiningDropsEnabled = config.Bind<bool>("04 - Mining Drops", "Extra Drops Per Rock", true, new ConfigDescription("Your pickaxe skill pays extra from the rocks and ore you mine.", (AcceptableValueBase)null, new object[1] { Switch() })); MiningMaxBonusRolls = config.Bind<float>("04 - Mining Drops", "Max Bonus Rolls", 4f, new ConfigDescription("How many bonus rolls a mined rock pays at skill 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Primary() })); MiningByProducts = Names(config.Bind<string>("04 - Mining Drops", "By-Products", "Stone", new ConfigDescription("Items the bonus skips only when the break also produced something else, comma separated. Stone here means an ore vein pays ore without the stone, while a plain stone rock still pays its stone. Use Excluded Items instead to refuse an item outright. Clear this to multiply everything a rock drops.", (AcceptableValueBase)null, new object[1] { AdminOnly() }))); ExcludedMiningItems = Exclusion(config, "04 - Mining Drops", "LeatherScraps, WitheredBone"); PlantingEnabled = config.Bind<bool>("05 - Planting", "Enabled", true, new ConfigDescription("Your farming skill lets you plant a patch at a time, line seeds up with the plants already there, and grow crops outside the biome they belong to.", (AcceptableValueBase)null, new object[1] { Switch() })); PlantExtraStamina = config.Bind<float>("05 - Planting", "Extra Plant Stamina Percent", 75f, new ConfigDescription("How much stamina each plant after the first costs, as a percentage of a normal planting. 100 charges full stamina for every plant, 0 charges none for the extras.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); PlantAnyBiomeAt = config.Bind<float>("05 - Planting", "Any Biome At", 60f, new ConfigDescription("The farming level at which crops you plant grow in any biome.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); PlantSnapStartsAt = config.Bind<float>("05 - Planting", "Snapping Starts At", 10f, new ConfigDescription("The farming level at which seeds start lining up with the plants already there.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); PlantRowUnlocks = Levels(config.Bind<string>("05 - Planting", "Row Unlocks", "15, 35, 60", new ConfigDescription("Farming levels that each add one to the longest side you can plant, comma separated. With 15, 35, 60 you can plant two in a row at 15, three at 35 and four at 60.", (AcceptableValueBase)null, new object[1] { Primary() }))); PlantWidthUnlocks = Levels(config.Bind<string>("05 - Planting", "Width Unlocks", "25, 50, 80", new ConfigDescription("Farming levels that each add one to the shortest side you can plant, comma separated. With 25, 50, 80 a patch can be two wide at 25, three at 50 and four at 80.", (AcceptableValueBase)null, new object[1] { Primary() }))); CycleShapeModifier = config.Bind<KeyboardShortcut>("05 - Planting", "Cycle Shape Modifier", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), new ConfigDescription("Hold this and scroll to change the planting shape.", (AcceptableValueBase)null, Array.Empty<object>())); CookingEnabled = config.Bind<bool>("06 - Cooking", "Enabled", true, new ConfigDescription("Your cooking skill holds a cooked food at its full value for part of its life. Off, food already carrying a plateau fades as it does in vanilla.", (AcceptableValueBase)null, new object[1] { Switch() })); CookPlateauPercent = config.Bind<float>("06 - Cooking", "Max Plateau Percent", 75f, new ConfigDescription("How long a cooked food holds its full value before it starts fading, as a percentage of its duration, at cooking level 100. With 75, a 30 minute food from a maxed cook stays at full value for its first 22 minutes, and for 11 at level 50.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); CookStackFoods = config.Bind<bool>("06 - Cooking", "Stack Foods", true, new ConfigDescription("Whether newly cooked food stacks with food already in your inventory. On, the merged stack is worth the average of the two. Off, food cooked at a different skill keeps a stack of its own.", (AcceptableValueBase)null, new object[1] { Primary() })); CraftingEnabled = config.Bind<bool>("07 - Crafting", "Enabled", true, new ConfigDescription("Your crafting skill stops rain wearing down what you build, pays a chance of a higher quality on what you make, and keeps your fires lit without fuel. Loading a smelter, kiln, furnace or refinery also raises it.", (AcceptableValueBase)null, new object[1] { Switch() })); BuildWeatherWearStopsAt = config.Bind<float>("07 - Crafting", "Weather Wear Stops At", 30f, new ConfigDescription("The crafting level at which rain stops wearing down and darkening what you build. Pieces already standing keep whatever their builder knew.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); CraftQualityChance = config.Bind<float>("07 - Crafting", "Quality Bonus Chance", 25f, new ConfigDescription("The chance at crafting level 100 that a weapon, tool or piece of armour comes out one quality level higher, whether freshly crafted or upgraded. Scales down with the level, and needs a station good enough to have made that quality. The Forge of Potential rolls its own odds and is not affected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); FireNoFuelAt = config.Bind<float>("07 - Crafting", "Fire Needs No Fuel At", 15f, new ConfigDescription("The crafting level at which fires you place keep burning without fuel. Fires already standing keep whatever their builder knew.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); FireAlwaysNeedsFuel = config.Bind<string>("07 - Crafting", "Always Needs Fuel", "piece_bathtub, piece_oven", new ConfigDescription("Pieces that keep needing fuel whatever your level, comma separated. Matched against a piece's name in whole or in part.", (AcceptableValueBase)null, new object[1] { Primary() })); StationCraftXp = config.Bind<float>("07 - Crafting", "Station Craft Xp", 0.2f, new ConfigDescription("The crafting skill gained for each ore or fuel put into a smelter, kiln, furnace, refinery, windmill or spinning wheel. An ordinary craft is worth 0.25. 0 pays nothing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { Primary() })); ForgeEnabled = config.Bind<bool>("08 - Forge of Potential", "Enabled", true, new ConfigDescription("Your crafting skill improves the odds at a station that can destroy an item it upgrades.", (AcceptableValueBase)null, new object[1] { Switch() })); UpgradeBreakImmunityAt = config.Bind<float>("08 - Forge of Potential", "Upgrade Break Immunity At", 40f, new ConfigDescription("The crafting level at which a station that can destroy an item it upgrades no longer can. The chance of destroying it falls away as the level approaches this. 0 leaves that chance as it is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); UpgradeSuccessBonus = config.Bind<float>("08 - Forge of Potential", "Upgrade Success Bonus", 30f, new ConfigDescription("Percentage points added at crafting level 100 to the chance such an upgrade succeeds rather than dropping the item two levels. Scales up from the level where destroying stops.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { Primary() })); PathfindingEnabled = config.Bind<bool>("09 - Pathfinding", "Enabled", true, new ConfigDescription("Pathfinding gains levels and pays its rewards. Turning it off removes the skill at the next restart, and any level it had reached is lost for good.", (AcceptableValueBase)null, new object[1] { Switch() })); PathSkillGainRate = config.Bind<float>("09 - Pathfinding", "Skill Gain Rate", 100f, new ConfigDescription("How fast Pathfinding levels, as a percentage of its normal rate. 0 stops it gaining entirely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[1] { Primary() })); PathAnnounce = config.Bind<bool>("09 - Pathfinding", "Announce Finds", true, new ConfigDescription("Show a card naming what you just found.", (AcceptableValueBase)null, new object[1] { Primary() })); PathExploreRadiusBonus = config.Bind<float>("09 - Pathfinding", "Explore Radius Bonus", 200f, new ConfigDescription("Percentage added at Pathfinding 100 to how far from you the map uncovers as you walk. With 200 the map uncovers 50 metres around you at Pathfinding 0 and 150 metres at Pathfinding 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 400f), new object[1] { Primary() })); PathSailBonus = config.Bind<float>("09 - Pathfinding", "Sailing Speed Bonus", 25f, new ConfigDescription("Percentage added at Pathfinding 100 to the speed of a ship you are steering. Everyone aboard sails faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), new object[1] { Primary() })); PathDungeonMaxBonusRolls = config.Bind<float>("09 - Pathfinding", "Dungeon Drop Rolls", 2f, new ConfigDescription("How many extra items picking up a single thing inside a dungeon pays at Pathfinding 100. A stack uses Dungeon Stack Percent instead. Nothing picked up out in the world is affected. 0 pays nothing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[1] { Primary() })); PathDungeonStackPercent = config.Bind<float>("09 - Pathfinding", "Dungeon Stack Percent", 100f, new ConfigDescription("The share of a stack picked up inside a dungeon that it pays extra at Pathfinding 100, as a percentage, rounded down and scaled by the level. With 100 a stack of five pays one more from Pathfinding 20 and a stack of four from 25. Picking up a single thing uses Dungeon Drop Rolls instead. 0 pays nothing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), new object[1] { Primary() })); ExcludedDungeonItems = Exclusion(config, "09 - Pathfinding"); LogBonusRolls = config.Bind<bool>("10 - Debug", "Log Bonus Rolls", false, new ConfigDescription("Write a line to the log for each kill that pays a bonus.", (AcceptableValueBase)null, Array.Empty<object>())); LogPlanting = config.Bind<bool>("10 - Debug", "Log Planting", false, new ConfigDescription("Write a line to the log for each patch planted, with the shape, every cell's verdict and what it cost.", (AcceptableValueBase)null, Array.Empty<object>())); LogCooking = config.Bind<bool>("10 - Debug", "Log Cooking", false, new ConfigDescription("Write a line to the log each time food is cooked, stacked or eaten, with the cook's skill and the plateau it earned. Run talented_food in the console for the food you are carrying right now.", (AcceptableValueBase)null, Array.Empty<object>())); static ConfigurationManagerAttributes AdminOnly() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown return new ConfigurationManagerAttributes { IsAdminOnly = true }; } static ConfigurationManagerAttributes Primary() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown return new ConfigurationManagerAttributes { IsAdminOnly = true, Order = 100 }; } static ConfigurationManagerAttributes Switch() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigurationManagerAttributes { IsAdminOnly = true, Order = 200 }; } } private static NameList Exclusion(ConfigFile config, string section, string defaults = "") { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown return Names(config.Bind<string>(section, "Excluded Items", defaults, new ConfigDescription("Items the bonus never pays, whatever else the break produced, comma separated. For anything a mod would rather hand out itself, and for anything that is not a reward. Use the prefab name, which dev_hover shows in game and the debug log prints.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }))); } private static NameList Names(ConfigEntry<string> entry) { return new NameList(entry); } private static LevelList Levels(ConfigEntry<string> entry) { return new LevelList(entry); } } internal static class CookHud { private static readonly Color Full = new Color(0.55f, 0.9f, 0.45f); internal static void Tint(Hud hud, Player player) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hud == (Object)null || (Object)(object)player == (Object)null || hud.m_foodTime == null) { return; } List<Food> foods = player.GetFoods(); int num = Mathf.Min(hud.m_foodTime.Length, foods.Count); for (int i = 0; i < num; i++) { if (!((Object)(object)hud.m_foodTime[i] == (Object)null) && AtFullValue(player, foods[i]) && !(((Graphic)hud.m_foodTime[i]).color != Color.white)) { ((Graphic)hud.m_foodTime[i]).color = Full; } } } private static bool AtFullValue(Player player, Food food) { if (food == null || food.m_item == null) { return false; } float num = CookPlateau.Stored(player, food.m_name); if (num <= 0f) { return false; } return Mathf.Clamp01(food.m_time / food.m_item.m_shared.m_foodBurnTime) >= 1f - num; } } [HarmonyPatch(typeof(Hud), "UpdateFood")] internal static class HudUpdateFoodPatch { private static void Postfix(Hud __instance, Player player) { CookHud.Tint(__instance, player); } } internal static class CookPlateau { private const string ItemKey = "talented.cookPlateau"; private const string FoodKey = "talented.cookPlateau."; private const string ZdoKey = "talented.cookPlateau.by."; private const float Ceiling = 0.95f; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; private static readonly Dictionary<string, bool> s_inPlateau = new Dictionary<string, bool>(); private static CookingStation s_station; private static int s_slot = -1; private static bool s_crafting; private static float s_craftPlateau; private static bool s_arriving; private static float s_arrivingPlateau; private static bool Logging => TalentedPlugin.Settings.LogCooking.Value; private static void Log(string line) { TalentedPlugin.Log.LogInfo((object)("cook: " + line)); } internal static float Minutes(float ticks) { float num = Game.m_foodRate; if (num <= 0f) { num = 1f; } return ticks / num / 60f; } internal static string Percent(float plateau) { return (plateau * 100f).ToString("0.#") + "%"; } internal static string Name(SharedData shared) { string text = ((Localization.instance == null) ? null : Localization.instance.Localize(shared.m_name)); if (!string.IsNullOrEmpty(text)) { return text; } return shared.m_name; } internal static float Plateau(float skillFactor) { if (!TalentedPlugin.Settings.CookingEnabled.Value) { return 0f; } float value = TalentedPlugin.Settings.CookPlateauPercent.Value; return Mathf.Clamp(skillFactor * value / 100f, 0f, 0.95f); } private static bool IsFood(ItemData item) { SharedData val = item?.m_shared; if (val != null && val.m_foodBurnTime > 0f) { if (!(val.m_food > 0f) && !(val.m_foodStamina > 0f)) { return val.m_foodEitr > 0f; } return true; } return false; } private static void Write(ItemData item, float plateau) { item.m_customData["talented.cookPlateau"] = plateau.ToString("R", Invariant); } internal static bool TryRead(ItemData item, out float plateau) { plateau = 0f; if (item == null) { return false; } if (!TalentedPlugin.Settings.CookingEnabled.Value) { return false; } if (!item.m_customData.TryGetValue("talented.cookPlateau", out var value)) { return false; } if (!float.TryParse(value, NumberStyles.Float, Invariant, out var result)) { return false; } plateau = Mathf.Clamp(result, 0f, 0.95f); return true; } private static void BlendCandidates(Inventory inventory, ItemData sample, int arriving, float incoming) { if (inventory == null || !IsFood(sample) || sample.m_shared.m_maxStackSize <= 1 || arriving <= 0 || !TalentedPlugin.Settings.CookStackFoods.Value) { return; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_shared.m_name != sample.m_shared.m_name || allItem.m_quality != sample.m_quality || allItem.m_worldLevel != sample.m_worldLevel || allItem.m_stack >= allItem.m_shared.m_maxStackSize) { continue; } int num = Mathf.Min(arriving, allItem.m_shared.m_maxStackSize - allItem.m_stack); if (num > 0) { float plateau; bool flag = TryRead(allItem, out plateau); if (!flag) { plateau = 0f; } float num2 = (plateau * (float)allItem.m_stack + incoming * (float)num) / (float)(allItem.m_stack + num); Write(allItem, num2); if (Logging && (!flag || !Mathf.Approximately(plateau, num2))) { Log($"{Name(allItem.m_shared)} x{allItem.m_stack} + {num} merge, " + Percent(plateau) + " + " + Percent(incoming) + " -> " + Percent(num2)); } } } } internal static void BeginSpawn(CookingStation station, int slot) { s_station = station; s_slot = slot; } internal static void EndSpawn() { s_station = null; s_slot = -1; } internal static void RecordCook(CookingStation station, Humanoid user, ItemData item) { Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)station == (Object)null || (Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !IsFood(item)) { return; } ZNetView component = ((Component)station).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } component.ClaimOwnership(); ZDO zDO = component.GetZDO(); if (zDO != null) { float num = Plateau(((Character)val).GetSkillFactor((SkillType)105)); zDO.Set("talented.cookPlateau.by." + ZDOMan.GetSessionID(), num); if (Logging) { Log(Name(item.m_shared) + " on, Cooking " + $"{((Character)val).GetSkillLevel((SkillType)105):0.#}, plateau " + Percent(num)); } } } internal static void StampSpawned(ItemDrop item) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)s_station == (Object)null || (Object)(object)localPlayer == (Object)null || (Object)(object)item == (Object)null || !IsFood(item.m_itemData)) { return; } ZNetView component = ((Component)s_station).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); if (val == null) { return; } long num = val.GetLong(ZDOVars.s_playerID + s_slot, 0L); float num2 = ((num == 0L) ? Plateau(((Character)localPlayer).GetSkillFactor((SkillType)105)) : val.GetFloat("talented.cookPlateau.by." + num, -1f)); if (num2 < 0f) { if (Logging) { Log($"{Name(item.m_itemData.m_shared)} off, no record for {num}"); } return; } Write(item.m_itemData, num2); if (Logging) { Log(Name(item.m_itemData.m_shared) + " off, plateau " + Percent(num2)); } } internal static void BeginArrival(float plateau) { s_arriving = true; s_arrivingPlateau = plateau; } internal static void EndArrival() { s_arriving = false; } internal static bool MayStack(ItemData held) { if (TalentedPlugin.Settings.CookStackFoods.Value || !s_arriving || !IsFood(held)) { return true; } if (!TryRead(held, out var plateau)) { plateau = 0f; } return Mathf.Abs(plateau - s_arrivingPlateau) < 0.001f; } internal static ItemData NextStack(Inventory inventory, string name, int quality, float worldLevel, ItemData found) { if (found == null || inventory == null || MayStack(found)) { return found; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != found && !(allItem.m_shared.m_name != name) && allItem.m_quality == quality && (float)allItem.m_worldLevel == worldLevel && allItem.m_stack < allItem.m_shared.m_maxStackSize && MayStack(allItem)) { return allItem; } } if (Logging) { if (!TryRead(found, out var plateau)) { plateau = 0f; } Log(Name(found.m_shared) + " new stack, " + Percent(s_arrivingPlateau) + " joins none of the " + Percent(plateau) + " already held"); } return null; } internal static bool MayMerge(Inventory inventory, ItemData item, int amount, int x, int y) { if (inventory == null || !IsFood(item) || item.m_shared.m_maxStackSize <= 1) { return true; } ItemData itemAt = inventory.GetItemAt(x, y); if (itemAt == null || itemAt == item || !itemAt.IsSameType(item)) { return true; } int num = Mathf.Min(itemAt.m_shared.m_maxStackSize - itemAt.m_stack, Mathf.Min(amount, item.m_stack)); if (num <= 0) { return true; } if (!TryRead(itemAt, out var plateau)) { plateau = 0f; } if (!TryRead(item, out var plateau2)) { plateau2 = 0f; } if (Mathf.Abs(plateau - plateau2) < 0.001f) { return true; } if (!TalentedPlugin.Settings.CookStackFoods.Value) { if (Logging) { Log(Name(itemAt.m_shared) + " drop refused, " + Percent(plateau) + " + " + Percent(plateau2)); } return false; } float plateau3 = (plateau * (float)itemAt.m_stack + plateau2 * (float)num) / (float)(itemAt.m_stack + num); Write(itemAt, plateau3); if (Logging) { Log($"{Name(itemAt.m_shared)} x{itemAt.m_stack} + {num} dropped, " + Percent(plateau) + " + " + Percent(plateau2) + " -> " + Percent(plateau3)); } return true; } internal static void BeginCrafting() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 s_crafting = false; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if (!((Object)(object)currentCraftingStation == (Object)null) && (int)currentCraftingStation.m_craftingSkill == 105) { s_crafting = true; s_craftPlateau = Plateau(((Character)localPlayer).GetSkillFactor((SkillType)105)); } } } internal static void EndCrafting() { s_crafting = false; } internal static void BeforeCraftedAdded(Inventory inventory, string prefabName, int arriving) { if (s_crafting && !((Object)(object)ObjectDB.instance == (Object)null)) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()); if (!((Object)(object)val == (Object)null)) { BeginArrival(s_craftPlateau); BlendCandidates(inventory, val.m_itemData, arriving, s_craftPlateau); } } } internal static void AfterCraftedAdded(Inventory inventory, ItemData item) { if (s_crafting && IsFood(item) && !TryRead(item, out var _)) { Write(item, s_craftPlateau); if (Logging && inventory != null && inventory.GetAllItems().Contains(item)) { Log($"{Name(item.m_shared)} x{item.m_stack} crafted, plateau " + Percent(s_craftPlateau)); } } } internal static void BeforeAdded(Inventory inventory, ItemData item) { if (item == null) { return; } if (s_crafting) { if (IsFood(item) && !TryRead(item, out var _)) { Write(item, s_craftPlateau); } } else { float num = (TryRead(item, out num) ? num : 0f); BeginArrival(num); BlendCandidates(inventory, item, item.m_stack, num); } } internal static void BeforeEaten(Player player, ItemData item) { if (item != null && !((Object)(object)item.m_dropPrefab == (Object)null)) { if (!TryRead(item, out var plateau)) { plateau = 0f; } player.m_customData["talented.cookPlateau." + ((Object)item.m_dropPrefab).name] = plateau.ToString("R", Invariant); s_inPlateau.Remove(((Object)item.m_dropPrefab).name); } } internal static void OnEaten(Player player, ItemData item, bool eaten) { if (Logging && eaten && item != null) { float foodBurnTime = item.m_shared.m_foodBurnTime; float num = Stored(player, ((Object)item.m_dropPrefab).name); Log("ate " + Name(item.m_shared) + ", plateau " + Percent(num) + ", " + $"{Minutes(foodBurnTime * num):0.#} of {Minutes(foodBurnTime):0.#} min"); } } internal static float Stored(Player player, string name) { if (name == null || !TalentedPlugin.Settings.CookingEnabled.Value || !player.m_customData.TryGetValue("talented.cookPlateau." + name, out var value)) { return 0f; } if (!float.TryParse(value, NumberStyles.Float, Invariant, out var result)) { return 0f; } return Mathf.Clamp(result, 0f, 0.95f); } internal static void Totals(Player player, ref float totalHp, ref float totalStamina, ref float totalEitr) { List<Food> foods = player.GetFoods(); bool flag = false; float num = player.m_baseHP; float num2 = player.m_baseStamina; float num3 = 0f; foreach (Food item in foods) { SharedData shared = item.m_item.m_shared; float num4 = Stored(player, item.m_name); if (num4 > 0f) { flag = true; float num5 = Mathf.Clamp01(item.m_time / shared.m_foodBurnTime); float num6 = ((num5 >= 1f - num4) ? 1f : Mathf.Pow(num5 / (1f - num4), 0.3f)); item.m_health = shared.m_food * num6; item.m_stamina = shared.m_foodStamina * num6; item.m_eitr = shared.m_foodEitr * num6; if (Logging) { NotePlateau(item, num5 >= 1f - num4); } } num += item.m_health; num2 += item.m_stamina; num3 += item.m_eitr; } Prune(player, foods); if (flag) { totalHp = num; totalStamina = num2; totalEitr = num3; } } private static void NotePlateau(Food food, bool inside) { bool value; bool flag = s_inPlateau.TryGetValue(food.m_name, out value); if (!flag || value != inside) { s_inPlateau[food.m_name] = inside; if (flag && value && !inside) { Log(Name(food.m_item.m_shared) + " plateau ended, " + $"{Minutes(food.m_time):0.#} min left"); } } } private static void Prune(Player player, List<Food> foods) { List<string> list = null; foreach (KeyValuePair<string, string> customDatum in player.m_customData) { if (!customDatum.Key.StartsWith("talented.cookPlateau.")) { continue; } string text = customDatum.Key.Substring("talented.cookPlateau.".Length); bool flag = false; foreach (Food food in foods) { if (!(food.m_name != text)) { flag = true; break; } } if (!flag) { if (list == null) { list = new List<string>(); } list.Add(customDatum.Key); } } if (list == null) { return; } foreach (string item in list) { player.m_customData.Remove(item); s_inPlateau.Remove(item.Substring("talented.cookPlateau.".Length)); } } } [HarmonyPatch(typeof(CookingStation), "CookItem")] internal static class CookingStationCookItemPatch { private static void Prefix(CookingStation __instance, Humanoid user, ItemData item) { CookPlateau.RecordCook(__instance, user, item); } } [HarmonyPatch(typeof(CookingStation), "SpawnItem")] internal static class CookingStationSpawnItemPatch { private static void Prefix(CookingStation __instance, int slot) { CookPlateau.BeginSpawn(__instance, slot); } private static void Finalizer() { CookPlateau.EndSpawn(); } } [HarmonyPatch(typeof(ItemDrop), "OnCreateNew", new Type[] { typeof(ItemDrop), typeof(bool) })] internal static class ItemDropOnCreateNewPatch { private static void Postfix(ItemDrop item) { CookPlateau.StampSpawned(item); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class InventoryGuiDoCraftingPatch { private static void Prefix() { CookPlateau.BeginCrafting(); } private static void Finalizer() { CookPlateau.EndCrafting(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool), typeof(bool), typeof(bool) })] internal static class InventoryAddCraftedPatch { private static void Prefix(Inventory __instance, string name, int stack) { CookPlateau.BeforeCraftedAdded(__instance, name, stack); } private static void Postfix(Inventory __instance, ItemData __result) { CookPlateau.AfterCraftedAdded(__instance, __result); } private static void Finalizer() { CookPlateau.EndArrival(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class InventoryAddItemPatch { private static void Prefix(Inventory __instance, ItemData item) { CookPlateau.BeforeAdded(__instance, item); } private static void Finalizer() { CookPlateau.EndArrival(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(Vector2i) })] internal static class InventoryAddItemAtPatch { private static void Prefix(Inventory __instance, ItemData item) { CookPlateau.BeforeAdded(__instance, item); } private static void Finalizer() { CookPlateau.EndArrival(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] internal static class InventoryAddItemToSlotPatch { private static bool Prefix(Inventory __instance, ItemData item, int amount, int x, int y, ref bool __result) { if (CookPlateau.MayMerge(__instance, item, amount, x, y)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "FindFreeStackItem")] internal static class InventoryFindFreeStackItemPatch { private static void Postfix(Inventory __instance, string name, int quality, float worldLevel, ref ItemData __result) { __result = CookPlateau.NextStack(__instance, name, quality, worldLevel, __result); } } [HarmonyPatch(typeof(Player), "EatFood")] internal static class PlayerEatFoodPatch { private static void Prefix(Player __instance, ItemData item) { CookPlateau.BeforeEaten(__instance, item); } private static void Postfix(Player __instance, ItemData item, bool __result) { CookPlateau.OnEaten(__instance, item, __result); } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class PlayerGetTotalFoodValuePatch { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { CookPlateau.Totals(__instance, ref hp, ref stamina, ref eitr); } } internal static class CraftBonus { private static readonly FieldRef<InventoryGui, Recipe> s_recipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe"); internal static int PerCraft(InventoryGui gui) { Recipe val = (((Object)(object)gui == (Object)null) ? null : s_recipe.Invoke(gui)); if (!((Object)(object)val == (Object)null)) { return Mathf.Max(1, val.m_amount); } return 1; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CraftBonusPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); FieldInfo fieldInfo = AccessTools.Field(typeof(InventoryGui), "m_craftBonusAmount"); MethodInfo methodInfo = AccessTools.Method(typeof(CraftBonus), "PerCraft", (Type[])null, (Type[])null); for (int i = 0; i < list.Count - 6; i++) { if (CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && !(list[i + 1].opcode != OpCodes.Add)) { int num = Index(list[i + 2]); if (num >= 0 && Index(list[i + 4]) == num) { CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null); val.labels.AddRange(list[i + 4].labels); list[i + 4] = val; list.Insert(i + 5, new CodeInstruction(OpCodes.Call, (object)methodInfo)); return list; } } } TalentedPlugin.Log.LogError((object)"CraftBonus: the bonus block was not found in DoCrafting; a bonus craft will grant one item, as in vanilla."); return list; } private static int Index(CodeInstruction instruction) { OpCode opcode = instruction.opcode; if (opcode == OpCodes.Ldloc_0 || opcode == OpCodes.Stloc_0) { return 0; } if (opcode == OpCodes.Ldloc_1 || opcode == OpCodes.Stloc_1) { return 1; } if (opcode == OpCodes.Ldloc_2 || opcode == OpCodes.Stloc_2) { return 2; } if (opcode == OpCodes.Ldloc_3 || opcode == OpCodes.Stloc_3) { return 3; } if (opcode != OpCodes.Ldloc && opcode != OpCodes.Ldloc_S && opcode != OpCodes.Stloc && opcode != OpCodes.Stloc_S) { return -1; } object operand = instruction.operand; if (!(operand is LocalVariableInfo localVariableInfo)) { if (operand is int) { return (int)operand; } return -1; } return localVariableInfo.LocalIndex; } } internal static class CraftQuality { private static readonly FieldRef<InventoryGui, Recipe> s_recipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe"); private static readonly FieldRef<InventoryGui, ItemData> s_upgrade = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_craftUpgradeItem"); private static int s_expect; private static int s_granted; internal static void BeforeCrafting(InventoryGui gui) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) s_expect = 0; s_granted = 0; if (!TalentedPlugin.Settings.CraftingEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)gui == (Object)null) { return; } Recipe val = s_recipe.Invoke(gui); if ((Object)(object)val == (Object)null || (Object)(object)val.m_item == (Object)null) { return; } SharedData shared = val.m_item.m_itemData.m_shared; if (shared == null || shared.m_maxQuality <= 1) { return; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || (int)currentCraftingStation.m_craftingSkill == 0 || currentCraftingStation.m_upgrader) { return; } ItemData val2 = s_upgrade.Invoke(gui); int num = ((val2 == null) ? 1 : (val2.m_quality + 1)); int num2 = num + 1; string text = ((val2 == null) ? $"fresh craft q{num} -> q{num2}" : $"UPGRADE of a q{val2.m_quality} -> q{num2}"); string text2 = $"{Utils.GetPrefabName(((Component)currentCraftingStation).gameObject)} lv{currentCraftingStation.GetLevel(true)}"; if (num2 > shared.m_maxQuality || val.GetRequiredStationLevel(num2) > currentCraftingStation.GetLevel(true)) { Report(shared.m_name + " at " + text2 + ", " + text + ": no roll - over max " + $"{shared.m_maxQuality}, or wants station level " + $"{val.GetRequiredStationLevel(num2)}"); return; } float num3 = TalentedPlugin.Settings.CraftQualityChance.Value / 100f; if (!(num3 <= 0f)) { float num4 = ((Character)localPlayer).GetSkillFactor(currentCraftingStation.m_craftingSkill) * num3; float value = Random.value; Report($"{shared.m_name} at {text2}, {text}: rolled {value:0.###} against " + string.Format("{0:0.##}%, {1}", num4 * 100f, (value < num4) ? "granted" : "declined")); if (!(value >= num4)) { s_expect = num; s_granted = num2; } } } private static void Report(string line) { if (TalentedPlugin.Settings.LogBonusRolls.Value) { TalentedPlugin.Log.LogInfo((object)("craftquality: " + line)); } } internal static void AfterCrafting() { s_expect = 0; s_granted = 0; } internal static void BeforeAdded(ref int quality) { if (s_granted > 0 && quality == s_expect) { quality = s_granted; s_granted = 0; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, "$talented_craftquality", 0, (Sprite)null, false); } } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CraftQualityPatch { private static void Prefix(InventoryGui __instance) { CraftQuality.BeforeCrafting(__instance); } private static void Finalizer() { CraftQuality.AfterCrafting(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool), typeof(bool), typeof(bool) })] internal static class InventoryAddCraftedQualityPatch { private static void Prefix(ref int quality) { CraftQuality.BeforeAdded(ref quality); } } internal static class CraftXp { private const float Gain = 1f; private static readonly FieldRef<InventoryGui, Recipe> s_recipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe"); private static readonly HashSet<ItemType> Gear = new HashSet<ItemType> { (ItemType)3, (ItemType)14, (ItemType)22, (ItemType)4, (ItemType)20, (ItemType)5, (ItemType)6, (ItemType)7, (ItemType)11, (ItemType)17, (ItemType)12 }; private static float s_factor; internal static void BeforeCrafting(InventoryGui gui) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) s_factor = 0f; if ((Object)(object)gui == (Object)null) { return; } Recipe val = s_recipe.Invoke(gui); if ((Object)(object)val == (Object)null || (Object)(object)val.m_item == (Object)null) { return; } SharedData shared = val.m_item.m_itemData.m_shared; if (shared == null || !Gear.Contains(shared.m_itemType) || (Object)(object)val.m_craftingStation == (Object)null) { return; } SkillType craftingSkill = val.m_craftingStation.m_craftingSkill; if ((int)craftingSkill == 0) { return; } float num = 1f; float num2 = StepOf(craftingSkill); if (!(num2 <= 0f) && !(num <= num2)) { s_factor = num / num2; if (TalentedPlugin.Settings.LogBonusRolls.Value) { TalentedPlugin.Log.LogInfo((object)($"craftxp: {shared.m_name} ({shared.m_itemType}) raises {craftingSkill} by {num:0.##} " + $"instead of {num2:0.##}, factor x{s_factor:0.##}")); } } } internal static void AfterCrafting() { s_factor = 0f; } internal static void Scale(ref float value) { if (s_factor > 0f) { value *= s_factor; } } internal static float StepOf(SkillType type) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) Skills val = (((Object)(object)Player.m_localPlayer == (Object)null) ? null : ((Character)Player.m_localPlayer).GetSkills()); if ((Object)(object)val == (Object)null || val.m_skills == null) { return -1f; } foreach (SkillDef skill in val.m_skills) { if (skill != null && skill.m_skill == type) { return skill.m_increseStep; } } return -1f; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CraftXpPatch { private static void Prefix(InventoryGui __instance) { CraftXp.BeforeCrafting(__instance); } private static void Finalizer() { CraftXp.AfterCrafting(); } } [HarmonyPatch(typeof(Player), "RaiseSkill")] internal static class PlayerRaiseSkillPatch { private static void Prefix(ref float value) { CraftXp.Scale(ref value); } } internal static class DeathPenalty { internal struct Held { public float Level; public float Accumulator; } internal static readonly FieldRef<Skills, Dictionary<SkillType, Skill>> SkillData = AccessTools.FieldRefAccess<Skills, Dictionary<SkillType, Skill>>("m_skillData"); private static readonly HashSet<SkillType> Exempt = new HashSet<SkillType> { (SkillType)13, (SkillType)12, (SkillType)100, (SkillType)101, (SkillType)102, (SkillType)103, (SkillType)104, (SkillType)105, (SkillType)106, (SkillType)107, (SkillType)110 }; private static string s_customRaw; private static HashSet<SkillType> s_custom = new HashSet<SkillType>(); internal static void RefreshCustom() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) string text = TalentedPlugin.Settings.AlsoExemptSkills.Value ?? ""; if (text == s_customRaw) { return; } s_customRaw = text; HashSet<SkillType> hashSet = new HashSet<SkillType>(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { if (Enum.TryParse<SkillType>(text2, ignoreCase: true, out SkillType result) && (int)result != 0) { hashSet.Add(result); } else { TalentedPlugin.Log.LogWarning((object)("'" + text2 + "' is not a skill this game knows - run talented_skills in the console for the names")); } } } s_custom = hashSet; } internal static bool IsExempt(SkillType type) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!Exempt.Contains(type)) { return s_custom.Contains(type); } return true; } internal static Dictionary<SkillType, Held> Snapshot(Skills skills) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || !TalentedPlugin.Settings.ExemptNonCombatSkills.Value) { return null; } Dictionary<SkillType, Skill> dictionary = SkillData.Invoke(skills); if (dictionary == null) { return null; } RefreshCustom(); Dictionary<SkillType, Held> dictionary2 = null; foreach (KeyValuePair<SkillType, Skill> item in dictionary) { if (IsExempt(item.Key) && item.Value != null) { dictionary2 = dictionary2 ?? new Dictionary<SkillType, Held>(); dictionary2[item.Key] = new Held { Level = item.Value.m_level, Accumulator = item.Value.m_accumulator }; } } return dictionary2; } internal static void Restore(Skills skills, Dictionary<SkillType, Held> held) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || held == null) { return; } Dictionary<SkillType, Skill> dictionary = SkillData.Invoke(skills); if (dictionary == null) { return; } foreach (KeyValuePair<SkillType, Held> item in held) { if (dictionary.TryGetValue(item.Key, out var value) && value != null) { value.m_level = item.Value.Level; value.m_accumulator = item.Value.Accumulator; } } } } [HarmonyPatch(typeof(Skills), "OnDeath")] internal static class SkillsOnDeathPatch { private static void Prefix(Skills __instance, out Dictionary<SkillType, DeathPenalty.Held> __state) { __state = DeathPenalty.Snapshot(__instance); } private static void Finalizer(Skills __instance, Dictionary<SkillType, DeathPenalty.Held> __state) { DeathPenalty.Restore(__instance, __state); } } internal static class FireFuel { private static string s_raw; private static readonly List<string> s_excluded = new List<string>(); private static Player s_tending; internal static void Apply(Fireplace fire) { ZNetView component = ((Component)fire).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null) { return; } ZDO zDO = component.GetZDO(); if (zDO == null) { return; } Player builder = BuildWear.Builder; if ((Object)(object)builder != (Object)null && zDO.GetFloat("talented.craftingAtBuilding", -1f) < 0f) { zDO.Set("talented.craftingAtBuilding", ((Character)builder).GetSkillLevel((SkillType)107)); } if (!TalentedPlugin.Settings.CraftingEnabled.Value) { return; } float num = zDO.GetFloat("talented.craftingAtBuilding", -1f); if (!(num < 0f) && !(num < TalentedPlugin.Settings.FireNoFuelAt.Value)) { string prefabName = Utils.GetPrefabName(((Component)fire).gameObject); if (Excluded(fire)) { Report(prefabName + " (" + fire.m_name + ") keeps its fuel - on the exclusion list"); return; } fire.m_infiniteFuel = true; Report($"{prefabName} ({fire.m_name}) needs no fuel - built at crafting {num:0.#}"); } } internal static void BeginTending(Humanoid user) { Player val = (Player)(object)((user is Player) ? user : null); s_tending = (((Object)(object)val == (Object)(object)Player.m_localPlayer) ? val : null); } internal static void EndTending() { s_tending = null; } internal static void Tended(Fireplace fire) { if ((Object)(object)fire == (Object)null) { return; } string prefabName = Utils.GetPrefabName(((Component)fire).gameObject); Player val = s_tending; if ((Object)(object)val == (Object)null) { return; } ZNetView component = ((Component)fire).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { Report(prefabName + " tended but has no valid ZNetView - nothing to stamp"); return; } ZDO zDO = component.GetZDO(); if (zDO == null) { Report(prefabName + " tended but has no ZDO - nothing to stamp"); return; } float skillLevel = ((Character)val).GetSkillLevel((SkillType)107); float num = zDO.GetFloat("talented.craftingAtBuilding", -1f); if (skillLevel <= num) { Report($"{prefabName} tended at crafting {skillLevel:0.#}, already marked {num:0.#} - left alone"); return; } component.ClaimOwnership(); zDO.Set("talented.craftingAtBuilding", skillLevel); Report($"{prefabName} tended at crafting {skillLevel:0.#}, was {num:0.#}"); Apply(fire); } private static void Report(string line) { if (TalentedPlugin.Settings.LogBonusRolls.Value) { TalentedPlugin.Log.LogInfo((object)("firefuel: " + line)); } } private static bool Excluded(Fireplace fire) { Refresh(); if (s_excluded.Count == 0) { return false; } string prefabName = Utils.GetPrefabName(((Component)fire).gameObject); string name = fire.m_name; foreach (string item in s_excluded) { if (Contains(prefabName, item) || Contains(name, item)) { return true; } } return false; } private static bool Contains(string haystack, string needle) { if (!string.IsNullOrEmpty(haystack)) { return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static void Refresh() { string text = TalentedPlugin.Settings.FireAlwaysNeedsFuel.Value ?? ""; if (text == s_raw) { return; } s_raw = text; s_excluded.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0) { s_excluded.Add(text2); } } } } [HarmonyPatch(typeof(Fireplace), "Awake")] internal static class FireFuelAwakePatch { private static void Postfix(Fireplace __instance) { FireFuel.Apply(__instance); } } [HarmonyPatch(typeof(Fireplace), "Interact")] internal static class FireFuelInteractPatch { [HarmonyPriority(800)] private static void Prefix(Humanoid user) { FireFuel.BeginTending(user); } private static void Finalizer() { FireFuel.EndTending(); } } [HarmonyPatch(typeof(Fireplace), "UseItem")] internal static class FireFuelUseItemPatch { [HarmonyPriority(800)] private static void Prefix(Humanoid user) { FireFuel.BeginTending(user); } private static void Finalizer() { FireFuel.EndTending(); } } [HarmonyPatch(typeof(Fireplace), "RPC_AddFuel")] internal static class FireFuelAddFuelPatch { private static void Postfix(Fireplace __instance) { FireFuel.Tended(__instance); } } internal class FoodCommand : ConsoleCommand { public override string Name => "talented_food"; public override string Help => "Shows what each food slot is worth now, what vanilla would give it, and which food you are carrying has a cooking plateau."; public override void Run(string[] args, Terminal context) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Write(context, "talented_food: load a character first."); return; } float skillLevel = ((Character)localPlayer).GetSkillLevel((SkillType)105); float plateau = CookPlateau.Plateau(((Character)localPlayer).GetSkillFactor((SkillType)105)); Write(context, "talented_food: up to " + $"{TalentedPlugin.Settings.CookPlateauPercent.Value:0.#}% at Cooking " + $"100. You are at Cooking {skillLevel:0.#}, so food you cook now holds " + "full value for " + CookPlateau.Percent(plateau) + " of its life."); Slots(context, localPlayer); Carried(context, localPlayer); } private static void Slots(Terminal context, Player player) { List<Food> foods = player.GetFoods(); if (foods.Count == 0) { Write(context, " no food eaten."); return; } float num = player.m_baseHP; float num2 = player.m_baseStamina; float num3 = 0f; float num4 = player.m_baseHP; float num5 = player.m_baseStamina; float num6 = 0f; Write(context, " -- food slots --"); foreach (Food item in foods) { SharedData shared = item.m_item.m_shared; float num7 = CookPlateau.Stored(player, item.m_name); float num8 = Mathf.Clamp01(item.m_time / shared.m_foodBurnTime); float num9 = Mathf.Pow(num8, 0.3f); bool flag = num7 > 0f && num8 >= 1f - num7; Write(context, " " + CookPlateau.Name(shared) + " " + ((num7 > 0f) ? (CookPlateau.Percent(num7) + " plateau") : "no plateau") + $" {CookPlateau.Minutes(item.m_time):0.#} of " + $"{CookPlateau.Minutes(shared.m_foodBurnTime):0.#} min left" + (flag ? " AT FULL VALUE" : "")); Write(context, $" health {item.m_health:0.#} (vanilla " + $"{shared.m_food * num9:0.#})" + $" stamina {item.m_stamina:0.#} (vanilla " + $"{shared.m_foodStamina * num9:0.#})" + $" eitr {item.m_eitr:0.#} (vanilla " + $"{shared.m_foodEitr * num9:0.#})"); num += item.m_health; num2 += item.m_stamina; num3 += item.m_eitr; num4 += shared.m_food * num9; num5 += shared.m_foodStamina * num9; num6 += shared.m_foodEitr * num9; } Write(context, $" totals: {num:0.#} hp / {num2:0.#} stamina / {num3:0.#} eitr" + $" (vanilla: {num4:0.#} / {num5:0.#} / {num6:0.#})"); } private static void Carried(Terminal context, Player player) { List<string> list = new List<string>(); foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { SharedData shared = allItem.m_shared; if (!(shared.m_foodBurnTime <= 0f) && (!(shared.m_food <= 0f) || !(shared.m_foodStamina <= 0f) || !(shared.m_foodEitr <= 0f))) { list.Add($" {allItem.m_stack}x {CookPlateau.Name(shared)} " + (CookPlateau.TryRead(allItem, out var plateau) ? (CookPlateau.Percent(plateau) + " plateau") : "no plateau - cooked before the mod, by someone else, or found in the world")); } } if (list.Count == 0) { return; } Write(context, " -- food you are carrying --"); foreach (string item in list) { Write(context, item); } } private static void Write(Terminal context, string line) { if (context != null) { context.AddString(line); } TalentedPlugin.Log.LogInfo((object)line); } } internal static class HarvestDrops { internal enum Stage { Trunk, Log, Sapling, Rock } private static HitData s_hit; private static Vector3 s_pos; private static Stage s_stage; private static bool s_rolling; private static string s_name; internal static void Open(HitData hit, Stage stage, Component source) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) s_hit = hit; s_pos = hit?.m_point ?? Vector3.zero; s_stage = stage; s_name = (((Object)(object)source == (Object)null) ? "?" : Utils.GetPrefabName(source.gameObject)); } internal static void Close() { s_hit = null; } internal static void Append(DropTable table, List<GameObject> result) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) if (s_rolling || s_hit == null || result == null || table == null || !Enabled()) { return; } HitData val = s_hit; if (val.m_skill != SkillFor(s_stage)) { Rejected(val); return; } float expected; float draw; int num = BonusRoll.Count(val.m_skillLevel, MaxRolls(), out expected, out draw); if (num <= 0) { Log(table, val, expected, draw, 0, null, 0); return; } bool flag = BonusRoll.WasLucky(expected, draw); int count = result.Count; List<GameObject> list = new List<GameObject>(); int num2 = 0; s_rolling = true; try { for (int i = 0; i < num; i++) { if (flag && i == num - 1) { num2 = list.Count; } foreach (GameObject drop in table.GetDropList()) { if (Visible(drop)) { list.Add(drop); } } } } finally { s_rolling = false; } bool flag2 = s_stage == Stage.Rock && MixedYield(result, count, list); Dictionary<string, int> dictionary = new Dictionary<string, int>(); int num3 = 0; for (int j = 0; j < list.Count; j++) { GameObject val2 = list[j]; if (!flag2 || !TalentedPlugin.Settings.MiningByProducts.Contains(((Object)val2).name)) { result.Add(val2); dictionary.TryGetValue(((Object)val2).name, out var value); dictionary[((Object)val2).name] = value + 1; if (flag && j >= num2) { num3++; } } } if (num3 > 0) { BonusEffect.Play(s_pos); } Log(table, val, expected, draw, num, dictionary, num3); } internal static void StampSkillLevel(HitData hit) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (hit != null && ((int)hit.m_skill == 13 || (int)hit.m_skill == 12)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)hit.GetAttacker() != (Object)(object)localPlayer)) { hit.m_skillLevel = ((Character)localPlayer).GetSkillLevel(hit.m_skill); } } } private static void Rejected(HitData hit) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (TalentedPlugin.Settings.LogBonusRolls.Value) { TalentedPlugin.Log.LogInfo((object)($"harvest: {s_name} opened as a {Label()}, which wants {SkillFor(s_stage)}, but " + $"the blow carried {hit.m_skill} - nothing paid")); } } internal static Stage StageFor(Destructible destructible, HitData hit) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) bool flag = Mineable(destructible); if (hit != null && (int)hit.m_skill == 12 && TalentedPlugin.Settings.LogBonusRolls.Value) { DamageModifiers damages = destructible.m_damages; TalentedPlugin.Log.LogInfo((object)("harvest: " + Utils.GetPrefabName(((Component)destructible).gameObject) + " is " + (flag ? "mineable" : "not mineable") + $" (pickaxe {damages.m_pickaxe}, chop {damages.m_chop})")); } if (!flag) { return Stage.Sapling; } return Stage.Rock; } private static bool Mineable(Destructible destructible) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)destructible == (Object)null) { return false; } DamageModifiers damages = destructible.m_damages; if (Takes(damages.m_pickaxe)) { return !Takes(damages.m_chop); } return false; } private static bool Takes(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)modifier != 3) { return (int)modifier != 4; } return false; } private static SkillType SkillFor(Stage stage) { if (stage == Stage.Rock) { return (SkillType)12; } return (SkillType)13; } private static bool Enabled() { if (s_stage != Stage.Rock) { return TalentedPlugin.Settings.TreeDropsEnabled.Value; } return TalentedPlugin.Settings.MiningDropsEnabled.Value; } private static float MaxRolls() { switch (s_stage) { case Stage.Log: case Stage.Sapling: return TalentedPlugin.Settings.LogMaxBonusRolls.Value; case Stage.Rock: return TalentedPlugin.Settings.MiningMaxBonusRolls.Value; default: return TalentedPlugin.Settings.TrunkMaxBonusRolls.Value; } } private static string Label() { return s_stage switch { Stage.Log => "log", Stage.Sapling => "sapling", Stage.Rock => "rock", _ => "trunk", }; } private static NameList Excluded() { if (s_stage == Stage.Rock) { return TalentedPlugin.Settings.ExcludedMiningItems; } return TalentedPlugin.Settings.ExcludedTreeItems; } private static bool MixedYield(List<GameObject> result, int vanillaCount, List<GameObject> candidates) { NameList miningByProducts = TalentedPlugin.Settings.MiningByProducts; if (miningByProducts.IsEmpty) { return false; } for (int i = 0; i < vanillaCount; i++) { GameObject val = result[i]; if (Visible(val) && !miningByProducts.Contains(((Object)val).name)) { return true; } } foreach (GameObject candidate in candidates) { if (!miningByProducts.Contains(((Object)candidate).name)) { return true; } } return false; } private static bool Visible(GameObject prefab) { if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null) { return false; } return !Excluded().Contains(((Object)prefab).name); } private static void Log(DropTable table, HitData hit, float expected, float draw, int rolls, Dictionary<string, int> paid, int luckyItems) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (TalentedPlugin.Settings.LogBonusRolls.Value) { int num = (int)expected; float num2 = expected - (float)num; TalentedPlugin.Log.LogInfo((object)($"{Label()}: {hit.m_skill} {hit.m_skillLevel:0.#} -> {expected:0.000} rolls " + $"({num} + {num2:0.000} into the interval = " + $"{BonusRoll.Chance(expected):0.000} chance, drew {draw:0.000}) " + $"-> {rolls} roll(s), {Describe(paid)} " + $"[table pays {table.m_dropMin}-{table.m_dropMax} at {table.m_dropChance:0.##}]" + ((luckyItems > 0) ? $", lucky roll paid {luckyItems} and sparked" : ""))); } } private static string Describe(Dictionary<string, int> paid) { if (paid == null || paid.Count == 0) { return "nothing"; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, int> item in paid) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(item.Key).Append(" x").Append(item.Value); } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(TreeBase), "RPC_Damage")] internal static class TreeBaseDamagePatch { private static void Prefix(TreeBase __instance, HitData hit) { HarvestDrops.Open(hit, HarvestDrops.Stage.Trunk, (Component)(object)__instance); } private static void Finalizer() { HarvestDrops.Close(); } } [HarmonyPatch(typeof(TreeLog), "Destroy", new Type[] { typeof(HitData), typeof(bool) })] internal static class TreeLogDestroyPatch { private static void Prefix(TreeLog __instance, HitData hitData) { HarvestDrops.Open(hitData, HarvestDrops.Stage.Log, (Component)(object)__instance); } private static void Finalizer() { HarvestDrops.Close(); } } [HarmonyPatch(typeof(Destructible), "Destroy")] internal static class DestructibleDestroyPatch { private static void Prefix(Destructible __instance, HitData hit) { HarvestDrops.Open(hit, HarvestDrops.StageFor(__instance, hit), (Component)(object)__instance); } private static void Finalizer() { HarvestDrops.Close(); } } [HarmonyPatch(typeof(MineRock5), "RPC_Damage")] internal static class MineRock5DamagePatch { private static void Prefix(MineRock5 __instance, HitData hit) { HarvestDrops.Open(hit, HarvestDrops.Stage.Rock, (Component)(object)__instance); } private static void Finalizer() { HarvestDrops.Close(); } } [HarmonyPatch(typeof(MineRock), "RPC_Hit")] internal static class MineRockHitPatch { private static void Prefix(MineRock __instance, HitData hit) { HarvestDrops.Open(hit, HarvestDrops.Stage.Rock, (Component)(object)__instance); } private static void Finalizer() { HarvestDrops.Close(); } } [HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { })] internal static class DropTableGetDropListPatch { private static void Postfix(DropTable __instance, List<GameObject> __result) { HarvestDrops.Append(__instance, __result); } } [HarmonyPatch(typeof(TreeBase), "Damage")] internal static class TreeBaseStampPatch { private static void Prefix(HitData hit) { HarvestDrops.StampSkillLevel(hit); } } [HarmonyPatch(typeof(TreeLog), "Damage")] internal static class TreeLogStampPatch { private static void Prefix(HitData hit) { HarvestDrops.StampSkillLevel(hit); } } [HarmonyPatch(typeof(Destructible), "Damage")] internal static class DestructibleStampPatch { private static void Prefix(HitData hit) { HarvestDrops.StampSkillLevel(hit); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5StampPatch { private static void Prefix(HitData hit) { HarvestDrops.StampSkillLevel(hit); } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockStampPatch { private static void Prefix(HitData hit) { HarvestDrops.StampSkillLevel(hit); } } internal sealed class LevelList { private static readonly int[] s_none = new int[0]; private readonly ConfigEntry<string> m_entry; private string m_raw; private int[] m_levels = s_none; internal int MaxTier { get { Refresh(); return 1 + m_levels.Length; } } internal LevelList(ConfigEntry<string> entry) { m_entry = entry; } internal int TierAt(float skillLevel) { Refresh(); int num = 1; int[] levels = m_levels; foreach (int num2 in levels) { if (skillLevel >= (float)num2) { num++; } } return num; } private void Refresh() { string text = m_entry.Value ?? ""; if (text == m_raw) { return; } m_raw = text; List<int> list = new List<int>(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0 && int.TryParse(text2, out var result) && result > 0) { list.Add(result); } } list.Sort(); m_levels = list.ToArray(); } } internal static class MassPlant { private static readonly Func<Player, float> s_buildStamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<Player, int> s_buildRemoveDebt = AccessTools.FieldRefAccess<Player, int>("m_buildRemoveDebt"); private static readonly FieldRef<Player, int> s_placeRotation = AccessTools.FieldRefAccess<Player, int>("m_placeRotation"); private static readonly Action<Player> s_setupGhost = AccessTools.MethodDelegate<Action<Player>>(AccessTools.Method(typeof(Player), "SetupPlacementGhost", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<Player, GameObject> s_ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost"); private static readonly FieldRef<Player, string> s_ghostLast = AccessTools.FieldRefAccess<Player, string>("m_placementGhostLast"); private static readonly FieldRef<Player, int> s_manualSnapPoint = AccessTools.FieldRefAccess<Player, int>("m_manualSnapPoint"); private static bool s_borrowing; private static readonly List<GameObject> s_ghosts = new List<GameObject>(); private static List<PlantCell> s_cells; private static PlantShape s_shape = PlantShape.Single; private static string s_ghostSource; private static float s_scroll; private static Piece s_justPlaced; private static List<PlantCell> s_plan; private static Quaternion s_planRotation; private static bool s_planting; private const int QuarterTurn = 4; internal static bool Borrowing => s_borrowing; internal static bool Planting => s_planting; internal static float BuildStamina(Player player) { return s_buildStamina(player); } internal static float ExtraStamina(Player player) { return s_buildStamina(player) * TalentedPlugin.Settings.PlantExtraStamina.Value / 100f; } private static Plant Selected(GameObject ghost) { if ((Object)(object)ghost == (Object)null || !ghost.activeSelf) { return null; } return ghost.GetComponent<Plant>(); } private static float FarmingLevel(Player player) { return ((Character)player).GetSkillLevel((SkillType)106); } internal static void TakeInput(Player player, GameObject ghost) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Selected(ghost) == (Object)null) { return; } List<PlantShape> list = PlantShape.Available(FarmingLevel(player)); s_shape = PlantShape.Remembered(player, list); KeyboardShortcut value = TalentedPlugin.Settings.CycleShapeModifier.Value; if (!((KeyboardShortcut)(ref value)).IsPressed() || list.Count < 2) { return; } s_scroll += ZInput.GetMouseScrollWheel(); int num = 0; if (s_scroll > player.m_scrollAmountThreshold) { num = 1; } else if (s_scroll < 0f - player.m_scrollAmountThreshold) { num = -1; } if (num != 0) { s_scroll = 0f; int num2 = list.IndexOf(s_shape); if (num2 < 0) { num2 = 0; } num2 = (num2 + num + list.Count) % list.Count; s_shape = list[num2]; PlantShape.Remember(player, s_shape); } } internal static void UpdateGhosts(Player player, GameObject ghost, ref PlacementStatus status) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected I4, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) Plant val = Selected(ghost); if ((Object)(object)val == (Object)null) { Hide(); return; } bool flag = TalentedPlugin.Settings.PlantingEnabled.Value && !ZInput.GetButton("AltPlace") && FarmingLevel(player) >= TalentedPlugin.Settings.PlantSnapStartsAt.Value; if (s_shape.IsSingle && !flag) { Hide(); s_cells = null; return; } EnsureGhosts(player, ghost, s_shape.Cells); s_cells = PlantGrid.Build(player, val, ghost.transform.position, ghost.transform.rotation, s_shape, flag); ghost.transform.position = s_cells[0].Pos; if (!s_cells[0].Plantable) { if ((int)status == 0) { status = (PlacementStatus)(int)s_cells[0].Status; } Piece component = ghost.GetComponent<Piece>(); if (component != null) { component.SetInvalidPlacementHeightlight(true); } } for (int i = 0; i < s_ghosts.Count && i + 1 < s_cells.Count; i++) { GameObject val2 = s_ghosts[i]; if (!((Object)(object)val2 == (Object)null)) { PlantCell plantCell = s_cells[i + 1]; val2.transform.position = plantCell.Pos; val2.transform.rotation = ghost.transform.rotation; val2.SetActive(true); Piece component2 = val2.GetComponent<Piece>(); if (component2 != null) { component2.SetInvalidPlacementHeightlight(!plantCell.Plantable); } } } } internal static void PlantExtras(Player player, PieceTable table, GameObject ghost) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) Piece val = s_justPlaced; List<PlantCell> list = s_plan; Quaternion val2 = s_planRotation; s_justPlaced = null; s_plan = null; if ((Object)(object)val == (Object)null) { return; } if ((Object)(object)((Component)val).GetComponent<Plant>() == (Object)null) { Trace("placed " + ((Object)((Component)val).gameObject).name + ", which is not a plant"); return; } if (list == null) { Trace("no cell list at the moment of placing"); return; } if (list.Count < 2) { PlantShape plantShape = s_shape; Trace("shape is " + plantShape.ToString() + ", so there is nothing to add"); return; } bool placementCostDisabled = player.PlacementCostDisabled; int num = 0; s_planting = true; try { for (int i = 1; i < list.Count; i++) { if (list[i].Plantable) { player.PlacePiece(val, list[i].Pos, val2, false, false); num++; if (!placementCostDisabled) { player.ConsumeResources(val.m_resources, 0, -1, 1); ((Character)player).UseStamina(ExtraStamina(player)); RaiseFarming(player, table); } } } } finally { s_planting = false; } if (TalentedPlugin.Settings.LogPlanting.Value) { StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < list.Count; j++) { stringBuilder.Append((j == 0) ? "[vanilla " : " [").Append(list[j].Status).Append(']'); } ManualLogSource log = TalentedPlugin.Log; string[] obj = new string[16] { "planted ", ((Object)((Component)val).gameObject).name, " as ", null, null, null, null, null, null, null, null, null, null, null, null, null }; PlantShape plantShape = s_shape; obj[3] = plantShape.T