using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CatAPI;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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;
}
}
}
internal static class PluginInfo
{
public const string PLUGIN_GUID = "com.yummi.dsp.deproliferation";
public const string PLUGIN_NAME = "Deproliferation";
public const string PLUGIN_VERSION = "0.1.1";
}
namespace Deproliferation
{
[HarmonyPatch]
internal static class AssemblerPatch
{
[HarmonyPrefix]
[HarmonyPatch(typeof(AssemblerComponent), "InternalUpdate")]
[Guard("470e8622e2ea8ccc")]
public static void InternalUpdate_Prefix(ref AssemblerComponent __instance, float power)
{
if (power < 0.1f || !Chance.Enabled)
{
return;
}
RecipeExecuteData recipeExecuteData = __instance.recipeExecuteData;
if (recipeExecuteData == null)
{
return;
}
if (__instance.extraTime >= recipeExecuteData.extraTimeSpend && Chance.Roll() != Outcome.Success)
{
__instance.extraTime -= recipeExecuteData.extraTimeSpend;
}
if (__instance.time >= recipeExecuteData.timeSpend && !OutputFull(ref __instance, recipeExecuteData.productCounts))
{
Outcome outcome = Chance.Roll();
Feedback.Report(__instance.produced, outcome);
switch (outcome)
{
case Outcome.Fail:
__instance.time -= recipeExecuteData.timeSpend;
__instance.replicating = true;
break;
case Outcome.Meltdown:
__instance.replicating = false;
__instance.extraSpeed = 0;
__instance.speedOverride = __instance.speed;
__instance.extraPowerRatio = 0;
__instance.time -= recipeExecuteData.timeSpend;
break;
}
}
}
private static bool OutputFull(ref AssemblerComponent a, int[] productCounts)
{
//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_0014: Invalid comparison between Unknown and I4
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Invalid comparison between Unknown and I4
int[] produced = a.produced;
for (int i = 0; i < productCounts.Length; i++)
{
ERecipeType recipeType = a.recipeType;
if ((int)recipeType != 1)
{
if ((int)recipeType == 4)
{
if (produced[i] > productCounts[i] * 9)
{
return true;
}
}
else if (produced[i] > productCounts[i] * 19)
{
return true;
}
}
else if (produced[i] + productCounts[i] > 100)
{
return true;
}
}
return false;
}
}
internal enum Outcome
{
Success,
Fail,
Meltdown
}
internal static class Chance
{
private static int seed = Environment.TickCount;
[ThreadStatic]
private static Random rng;
private static Random Rng => rng ?? (rng = new Random(Interlocked.Add(ref seed, 625341585)));
internal static bool Enabled => Settings.FailPermille > 0;
internal static Outcome Roll()
{
int failPermille = Settings.FailPermille;
if (failPermille <= 0 || Rng.Next(1000) >= failPermille)
{
return Outcome.Success;
}
if (Rng.Next(1000) >= Settings.MeltdownPermille)
{
return Outcome.Fail;
}
return Outcome.Meltdown;
}
}
internal static class Feedback
{
private const int MaxTipsPerFrame = 3;
private const float HeadHeight = 3f;
private static volatile int[] watchedProduced;
private static volatile bool watchForge;
private static readonly ConcurrentQueue<Outcome> pending = new ConcurrentQueue<Outcome>();
internal static void Report(int[] produced, Outcome outcome)
{
if (produced != null && produced == watchedProduced)
{
pending.Enqueue(outcome);
}
}
internal static void ReportForge(Outcome outcome)
{
if (watchForge)
{
pending.Enqueue(outcome);
}
}
internal static void Tick()
{
UIGame val = ((!Settings.ShowTips || !GameMain.isRunning) ? null : UIRoot.instance?.uiGame);
watchedProduced = (((Object)(object)val == (Object)null) ? null : FindWatched(val));
watchForge = (Object)(object)val != (Object)null && (Object)(object)val.replicator != (Object)null && ((ManualBehaviour)val.replicator).active;
int num = 0;
Outcome result;
while (pending.TryDequeue(out result))
{
if ((Object)(object)val != (Object)null && num < 3)
{
Show(result);
num++;
}
}
}
private static int[] FindWatched(UIGame ui)
{
FactorySystem val = GameMain.localPlanet?.factory?.factorySystem;
if (val == null)
{
return null;
}
UIAssemblerWindow assemblerWindow = ui.assemblerWindow;
if ((Object)(object)assemblerWindow != (Object)null && ((ManualBehaviour)assemblerWindow).active)
{
int assemblerId = assemblerWindow.assemblerId;
if (assemblerId > 0 && assemblerId < val.assemblerCursor && val.assemblerPool[assemblerId].id == assemblerId)
{
return val.assemblerPool[assemblerId].produced;
}
}
UILabWindow labWindow = ui.labWindow;
if ((Object)(object)labWindow != (Object)null && ((ManualBehaviour)labWindow).active)
{
int labId = labWindow.labId;
if (labId > 0 && labId < val.labCursor && val.labPool[labId].id == labId && !val.labPool[labId].researchMode)
{
return val.labPool[labId].produced;
}
}
return null;
}
private static void Show(Outcome outcome)
{
//IL_0018: 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_001f: 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_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)
string text = Text(outcome);
Player mainPlayer = GameMain.mainPlayer;
if (mainPlayer != null && GameMain.localPlanet != null)
{
Vector3 position = mainPlayer.position;
UIRealtimeTip.Popup(text, position + ((Vector3)(ref position)).normalized * 3f, false, 0);
}
else
{
UIRealtimeTip.Popup(text, false, 0);
}
}
private static string Text(Outcome outcome)
{
bool isZHCN = Localization.isZHCN;
switch (outcome)
{
case Outcome.Fail:
if (!isZHCN)
{
return "Failed!";
}
return "失败!";
case Outcome.Meltdown:
if (!isZHCN)
{
return "Meltdown!";
}
return "融毁!";
default:
if (!isZHCN)
{
return "Success!";
}
return "成功!";
}
}
}
[HarmonyPatch]
internal static class ForgePatch
{
internal static bool Ready;
[ThreadStatic]
private static bool inTick;
[ThreadStatic]
private static bool rolled;
[ThreadStatic]
private static Outcome outcome;
[ThreadStatic]
private static bool meltdownHappened;
private static Outcome Current()
{
if (!rolled)
{
outcome = Chance.Roll();
rolled = true;
}
return outcome;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(MechaForge), "GameTick")]
[Guard("1b63527b1492f2a3")]
public static void GameTick_Prefix(MechaForge __instance)
{
inTick = false;
rolled = false;
meltdownHappened = false;
if (Ready && Chance.Enabled)
{
inTick = true;
RefillOrCancel(__instance);
}
}
[HarmonyFinalizer]
[HarmonyPatch(typeof(MechaForge), "GameTick")]
[Guard("1b63527b1492f2a3")]
public static void GameTick_Finalizer(MechaForge __instance)
{
inTick = false;
rolled = false;
if (meltdownHappened && __instance.tasks != null)
{
__instance.CalculateExtra();
}
meltdownHappened = false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Mecha), "AddConsumptionStat")]
[Guard("d43e4cb17b432c06")]
public static bool AddConsumptionStat_Prefix()
{
if (inTick)
{
return Current() != Outcome.Fail;
}
return true;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Mecha), "AddProductionStat")]
[Guard("b343fa55a107408d")]
public static bool AddProductionStat_Prefix()
{
if (inTick)
{
return Current() == Outcome.Success;
}
return true;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ForgeTask), "Produce")]
[Guard("76f145124b84fec0")]
public static bool Produce_Prefix(ForgeTask __instance)
{
if (!inTick)
{
return true;
}
Outcome outcome = Current();
rolled = false;
Feedback.ReportForge(outcome);
switch (outcome)
{
case Outcome.Fail:
__instance.tick -= __instance.tickSpend;
return false;
case Outcome.Meltdown:
{
for (int i = 0; i < __instance.itemCounts.Length; i++)
{
__instance.served[i] -= __instance.itemCounts[i];
}
__instance.count--;
__instance.tick -= __instance.tickSpend;
meltdownHappened = true;
return false;
}
default:
return true;
}
}
private static void RefillOrCancel(MechaForge forge)
{
if (forge.tasks == null || forge.tasks.Count == 0 || forge.player == null || !forge.player.isAlive)
{
return;
}
ForgeTask val = forge.tasks[0];
if (val.itemEnough)
{
return;
}
StorageComponent package = forge.player.package;
if (package != null)
{
int num4 = default(int);
for (int i = 0; i < val.itemIds.Length; i++)
{
int num = val.itemCounts[i] * val.count - val.served[i];
if (num > 0)
{
int num2 = val.itemIds[i];
int num3 = num;
package.TakeTailItems(ref num2, ref num3, ref num4, false);
val.served[i] += num3;
}
}
}
if (!val.itemEnough)
{
forge.CancelTask(0);
UIRealtimeTip.Popup(Localization.isZHCN ? "融毁损失了中间产物,背包里不够补,已取消该制造任务" : "A meltdown lost intermediate items and the inventory cannot cover it; the craft was cancelled", true, 0);
}
forge.CalculateExtra();
}
}
[HarmonyPatch]
internal static class LabPatch
{
[HarmonyPrefix]
[HarmonyPatch(typeof(LabComponent), "InternalUpdateAssemble")]
[Guard("614a888f1ca1f96c")]
public static void InternalUpdateAssemble_Prefix(ref LabComponent __instance, float power)
{
if (power < 0.1f || !Chance.Enabled)
{
return;
}
RecipeExecuteData recipeExecuteData = __instance.recipeExecuteData;
if (recipeExecuteData == null)
{
return;
}
if (__instance.extraTime >= recipeExecuteData.extraTimeSpend && Chance.Roll() != Outcome.Success)
{
__instance.extraTime -= recipeExecuteData.extraTimeSpend;
}
if (__instance.time >= recipeExecuteData.timeSpend && !OutputFull(ref __instance, recipeExecuteData.productCounts))
{
Outcome outcome = Chance.Roll();
Feedback.Report(__instance.produced, outcome);
switch (outcome)
{
case Outcome.Fail:
__instance.time -= recipeExecuteData.timeSpend;
__instance.replicating = true;
break;
case Outcome.Meltdown:
__instance.replicating = false;
__instance.extraSpeed = 0;
__instance.speedOverride = __instance.speed;
__instance.extraPowerRatio = 0;
__instance.time -= recipeExecuteData.timeSpend;
break;
}
}
}
private static bool OutputFull(ref LabComponent lab, int[] productCounts)
{
int num = 10 * ((lab.speedOverride + 9999) / 10000);
for (int i = 0; i < productCounts.Length; i++)
{
if (lab.produced[i] + productCounts[i] > num)
{
return true;
}
}
return false;
}
}
[BepInPlugin("com.yummi.dsp.deproliferation", "Deproliferation", "0.1.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.yummi.dsp.deproliferation";
public const string PluginName = "Deproliferation";
public const string PluginVersion = "0.1.1";
private static readonly Harmony harmony = new Harmony("com.yummi.dsp.deproliferation");
private static readonly PatchOptions HarnessOptions = new PatchOptions
{
ModName = "Deproliferation"
};
internal static ManualLogSource Log;
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
Settings.Bind(((BaseUnityPlugin)this).Config);
Harness.PatchAll(harmony, typeof(AssemblerPatch), HarnessOptions);
Harness.PatchAll(harmony, typeof(LabPatch), HarnessOptions);
Harness.PatchAll(harmony, typeof(ForgePatch), HarnessOptions);
ForgePatch.Ready = IsPatched(AccessTools.Method(typeof(MechaForge), "GameTick", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(ForgeTask), "Produce", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(Mecha), "AddProductionStat", (Type[])null, (Type[])null)) && IsPatched(AccessTools.Method(typeof(Mecha), "AddConsumptionStat", (Type[])null, (Type[])null));
if (!ForgePatch.Ready)
{
Log.LogWarning((object)"机甲制造台的补丁没有全部应用,手搓制造不会失败。");
}
Log.LogInfo((object)string.Format("{0} v{1} 已加载:失败率 {2}‰,失败中融毁占 {3}‰", "Deproliferation", "0.1.1", Settings.FailPermille, Settings.MeltdownPermille));
}
private void Update()
{
Feedback.Tick();
}
private static bool IsPatched(MethodBase method)
{
Patches val = ((method == null) ? null : Harmony.GetPatchInfo(method));
if (val != null)
{
return val.Owners.Contains("com.yummi.dsp.deproliferation");
}
return false;
}
private void OnDestroy()
{
harmony.UnpatchSelf();
}
}
internal static class Settings
{
internal const int PermilleMax = 1000;
internal const int DefaultPermille = 500;
internal const int ConfigSchemaVersion = 1;
private static ConfigEntry<int> failPermille;
private static ConfigEntry<int> meltdownPermille;
private static ConfigEntry<bool> showTips;
internal static volatile int FailPermille;
internal static volatile int MeltdownPermille;
internal static bool ShowTips => showTips.Value;
private static string Desc(string en, string zh)
{
return en + "\n" + zh;
}
internal static void Bind(ConfigFile config)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Expected O, but got Unknown
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Expected O, but got Unknown
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Expected O, but got Unknown
Harness.ApplyConfigSchema(config, 1, new ConfigSchemaOptions
{
WipeIfBelow = 1
});
AcceptableValueRange<int> val = new AcceptableValueRange<int>(0, 1000);
failPermille = config.Bind<int>("Chance", "FailPermille", 500, new ConfigDescription(Desc("Chance (per mille, integer 0-1000) that a finished craft fails. 500 = half.", "每次制造完成时失败的概率(千分比,整数 0–1000)。500 即一半。"), (AcceptableValueBase)(object)val, Array.Empty<object>()));
meltdownPermille = config.Bind<int>("Chance", "MeltdownPermille", 500, new ConfigDescription(Desc("Of the failed crafts, the chance (per mille, integer 0-1000) that it is a meltdown. Failure: no product, ingredients stay and the craft retries. Meltdown: no product and the ingredients are lost.", "在已经失败的制造里,变成融毁的概率(千分比,整数 0–1000)。失败:没有产物,原料还在,自动重做。融毁:没有产物,原料也消失。"), (AcceptableValueBase)(object)val, Array.Empty<object>()));
showTips = config.Bind<bool>("Feedback", "ShowTips", true, Desc("While a machine's window (or the replicator window) is open, pop Success! / Failed! / Meltdown! above Icarus on each craft.", "打开某台机器的面板(或机甲制造台面板)时,它每制造一次就在伊卡洛斯头顶弹出 成功!/ 失败!/ 融毁!"));
Harness.PruneConfigKeys(config, (ConfigDefinition[])(object)new ConfigDefinition[4]
{
((ConfigEntryBase)failPermille).Definition,
((ConfigEntryBase)meltdownPermille).Definition,
((ConfigEntryBase)showTips).Definition,
new ConfigDefinition("_ConfigSchema", "Version")
});
Refresh();
failPermille.SettingChanged += delegate
{
Refresh();
};
meltdownPermille.SettingChanged += delegate
{
Refresh();
};
}
private static void Refresh()
{
FailPermille = Clamp(failPermille.Value);
MeltdownPermille = Clamp(meltdownPermille.Value);
}
private static int Clamp(int value)
{
if (value < 0)
{
return 0;
}
if (value <= 1000)
{
return value;
}
return 1000;
}
}
}