using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("Hand Crafted Trinkets")]
[assembly: AssemblyCompany("Loki Moon Labs")]
[assembly: AssemblyProduct("Hand Crafted Trinkets")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: InternalsVisibleTo("HandCraftedTrinkets.Tests")]
[assembly: AssemblyVersion("0.1.1.0")]
namespace LokiMoonLabs.HandCraftedTrinkets;
[BepInPlugin("LokiMoonLabs.HandCraftedTrinkets", "Hand Crafted Trinkets", "0.1.1")]
[BepInIncompatibility("Appun.DSP.plugin.DSPCompleteReplicatorQueue")]
public sealed class Plugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(UIReplicatorWindow), "_OnOpen")]
private static class OpenReplicator
{
[HarmonyPrefix]
[HarmonyPriority(800)]
private static void Prefix()
{
if ((Object)(object)instance != (Object)null)
{
instance.EnableRecipes();
}
}
[HarmonyPostfix]
private static void Postfix(UIReplicatorWindow __instance)
{
if ((Object)(object)instance == (Object)null)
{
return;
}
try
{
instance.button.Open(__instance);
}
catch (Exception ex)
{
instance.button.Dispose();
((BaseUnityPlugin)instance).Logger.LogError((object)("Could not create Complete Queue button: " + ex));
}
}
}
[HarmonyPatch(typeof(UIReplicatorWindow), "_OnUpdate")]
private static class UpdateReplicator
{
[HarmonyPostfix]
private static void Postfix(UIReplicatorWindow __instance)
{
if ((Object)(object)instance != (Object)null)
{
instance.button.Refresh(__instance);
}
}
}
[HarmonyPatch(typeof(UIReplicatorWindow), "_OnFree")]
private static class FreeReplicator
{
[HarmonyPrefix]
private static void Prefix()
{
if ((Object)(object)instance != (Object)null)
{
instance.button.Dispose();
}
}
}
[HarmonyPatch(typeof(UIReplicatorWindow), "_OnDestroy")]
private static class DestroyReplicator
{
[HarmonyPrefix]
private static void Prefix()
{
if ((Object)(object)instance != (Object)null)
{
instance.button.Dispose();
}
}
}
public const string Guid = "LokiMoonLabs.HandCraftedTrinkets";
private static Plugin instance;
private readonly RecipeAccess recipes = new RecipeAccess();
private Harmony harmony;
private readonly QueueCompleter completion = new QueueCompleter();
private ReplicatorButton button;
private void Awake()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
instance = this;
button = new ReplicatorButton(Complete, (MechaForge forge) => !completion.Busy && Current(forge));
harmony = new Harmony("LokiMoonLabs.HandCraftedTrinkets");
harmony.PatchAll(typeof(Plugin).Assembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Hand Crafted Trinkets ready. Open the normal replicator to hand craft unlocked recipes with their normal ingredients.");
}
private static bool Current(MechaForge forge)
{
if (forge != null && GameMain.isRunning && !GameMain.isLoading && !GameMain.isPaused && GameMain.mainPlayer != null && forge.player == GameMain.mainPlayer && forge.player.isAlive)
{
return forge.mecha != null;
}
return false;
}
private static void Advance(MechaForge forge, long tick)
{
forge.GameTick(tick, 0f);
}
private void Complete(MechaForge forge)
{
if (!completion.Busy && Current(forge))
{
CompletionResult completionResult = completion.Drain(forge, GameMain.gameTick, Advance, Current);
((BaseUnityPlugin)this).Logger.LogInfo((object)("Complete Queue: " + completionResult.Cycles + " native production cycles completed."));
if (completionResult.Reason != null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)completionResult.Reason);
UIRealtimeTip.Popup(completionResult.Reason, false, 0);
}
if (completionResult.Error != null)
{
((BaseUnityPlugin)this).Logger.LogError((object)completionResult.Error);
}
}
}
private void EnableRecipes()
{
RecipeProtoSet val = LDB.recipes;
if (!((Object)(object)val == (Object)null))
{
int num = recipes.Enable(((ProtoSet<RecipeProto>)(object)val).dataArray);
if (num > 0)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Enabled hand crafting for " + num + " additional recipes. Ingredients, outputs, crafting times and research requirements are unchanged."));
}
}
}
private void OnDestroy()
{
if (harmony != null)
{
harmony.UnpatchSelf();
}
recipes.Restore();
if (button != null)
{
button.Dispose();
}
if ((Object)(object)instance == (Object)(object)this)
{
instance = null;
}
}
}
internal sealed class CompletionResult
{
internal int Cycles;
internal string Reason;
internal Exception Error;
}
internal sealed class QueueCompleter
{
internal bool Busy { get; private set; }
internal static bool Valid(ForgeTask task)
{
if (task == null || task.count <= 0 || task.tickSpend < 0)
{
return false;
}
if (!Amounts(task.itemIds, task.itemCounts, task.served, output: false))
{
return false;
}
return Amounts(task.productIds, task.productCounts, task.produced, output: true);
}
private static bool Amounts(int[] ids, int[] amounts, int[] buffered, bool output)
{
if (ids == null || amounts == null || buffered == null || ids.Length != amounts.Length || ids.Length != buffered.Length)
{
return false;
}
for (int i = 0; i < ids.Length; i++)
{
if (ids[i] <= 0 || amounts[i] <= 0 || buffered[i] < 0 || (output && buffered[i] != 0))
{
return false;
}
}
return true;
}
internal CompletionResult Drain(MechaForge forge, long tick, Action<MechaForge, long> advance, Func<MechaForge, bool> current)
{
CompletionResult completionResult = new CompletionResult();
if (Busy)
{
completionResult.Reason = "Queue completion is already running.";
return completionResult;
}
if (forge == null || forge.tasks == null || advance == null || current == null || !current(forge))
{
completionResult.Reason = "Load a game and resume play before completing the queue.";
return completionResult;
}
Busy = true;
try
{
Dictionary<ForgeTask, int> dictionary = new Dictionary<ForgeTask, int>();
for (int i = 0; i < forge.tasks.Count; i++)
{
ForgeTask val = forge.tasks[i];
if (!Valid(val) || dictionary.ContainsKey(val) || (val.parentTaskIndex >= 0 && (val.parentTaskIndex <= i || val.parentTaskIndex >= forge.tasks.Count)))
{
completionResult.Reason = "Invalid task state; the queue was left unchanged.";
return completionResult;
}
dictionary.Add(val, val.count);
}
while (forge.tasks.Count > 0)
{
if (!current(forge))
{
completionResult.Reason = "Game state changed; remaining work was preserved.";
break;
}
ForgeTask val2 = forge.tasks[0];
if (val2 == null || !dictionary.TryGetValue(val2, out var value) || val2.count > value)
{
completionResult.Reason = "The queue changed; click Complete Queue again for new work.";
break;
}
if (!Valid(val2) || !val2.itemEnough || (val2.parentTaskIndex >= 0 && (val2.parentTaskIndex == 0 || val2.parentTaskIndex >= forge.tasks.Count || !dictionary.ContainsKey(forge.tasks[val2.parentTaskIndex]))))
{
completionResult.Reason = "Missing inputs or invalid task state; remaining work was preserved.";
break;
}
int count = val2.count;
int tick2 = val2.tick;
val2.tick = Math.Max(tick2, val2.tickSpend);
try
{
advance(forge, tick);
}
catch (Exception error)
{
if (val2.count == count)
{
val2.tick = tick2;
}
completionResult.Error = error;
completionResult.Reason = "Native crafting raised an error; completion stopped.";
break;
}
if (val2.count != count - 1)
{
if (val2.count == count)
{
val2.tick = tick2;
}
completionResult.Reason = "Native crafting made no expected progress; remaining work was preserved.";
break;
}
dictionary[val2] = val2.count;
completionResult.Cycles++;
if (completionResult.Cycles == int.MaxValue)
{
completionResult.Reason = "Cycle limit reached; remaining work was preserved.";
break;
}
}
}
finally
{
Busy = false;
}
return completionResult;
}
}
internal sealed class RecipeAccess
{
private readonly HashSet<RecipeProto> changed = new HashSet<RecipeProto>();
internal static bool Eligible(RecipeProto recipe)
{
if (recipe != null && ((Proto)recipe).ID > 0 && recipe.TimeSpend > 0 && recipe.TimeSpend <= 214748 && ValidAmounts(recipe.Items, recipe.ItemCounts))
{
return ValidAmounts(recipe.Results, recipe.ResultCounts);
}
return false;
}
private static bool ValidAmounts(int[] ids, int[] counts)
{
if (ids == null || counts == null || ids.Length == 0 || ids.Length != counts.Length)
{
return false;
}
for (int i = 0; i < ids.Length; i++)
{
if (ids[i] <= 0 || counts[i] <= 0)
{
return false;
}
}
return true;
}
internal int Enable(RecipeProto[] recipes)
{
int num = 0;
if (recipes == null)
{
return num;
}
foreach (RecipeProto val in recipes)
{
if (val != null && !val.Handcraft && Eligible(val))
{
changed.Add(val);
val.Handcraft = true;
num++;
}
}
return num;
}
internal void Restore()
{
foreach (RecipeProto item in changed)
{
item.Handcraft = false;
}
changed.Clear();
}
}
internal sealed class ReplicatorButton : IDisposable
{
private static readonly FieldInfo ForgeField = AccessTools.Field(typeof(UIReplicatorWindow), "mechaForge");
private readonly Action<MechaForge> complete;
private readonly Func<MechaForge, bool> available;
private UIReplicatorWindow owner;
private Button button;
internal ReplicatorButton(Action<MechaForge> complete, Func<MechaForge, bool> available)
{
this.complete = complete;
this.available = available;
}
private static MechaForge Forge(UIReplicatorWindow window)
{
if (!((Object)(object)window == (Object)null) && !(ForgeField == null))
{
object? value = ForgeField.GetValue(window);
return (MechaForge)((value is MechaForge) ? value : null);
}
return null;
}
internal void Open(UIReplicatorWindow window)
{
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_0166: Unknown result type (might be due to invalid IL or missing references)
//IL_016b: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_0233: Unknown result type (might be due to invalid IL or missing references)
//IL_023a: Expected O, but got Unknown
//IL_0287: Unknown result type (might be due to invalid IL or missing references)
//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
//IL_02be: Unknown result type (might be due to invalid IL or missing references)
//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
//IL_0329: Unknown result type (might be due to invalid IL or missing references)
//IL_032b: Unknown result type (might be due to invalid IL or missing references)
//IL_0346: Unknown result type (might be due to invalid IL or missing references)
//IL_034d: Expected O, but got Unknown
UnityAction val = null;
if ((Object)(object)owner != (Object)(object)window || (Object)(object)button == (Object)null)
{
Dispose();
owner = window;
Transform val2 = ((Component)window).transform.Find("panel-bg");
if ((Object)(object)val2 == (Object)null)
{
val2 = ((Component)window).transform;
}
GameObject val3 = new GameObject("Hand Crafted Trinkets Complete Queue", new Type[3]
{
typeof(RectTransform),
typeof(Image),
typeof(Button)
});
val3.transform.SetParent(val2, false);
RectTransform component = val3.GetComponent<RectTransform>();
Transform obj = val2.Find("btn-box");
RectTransform val4 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
component.anchorMin = (((Object)(object)val4 == (Object)null) ? Vector2.one : val4.anchorMin);
component.anchorMax = (((Object)(object)val4 == (Object)null) ? Vector2.one : val4.anchorMax);
component.pivot = new Vector2(1f, 0.5f);
component.sizeDelta = new Vector2(235f, 30f);
component.anchoredPosition = new Vector2(-28f, -65f);
Image component2 = val3.GetComponent<Image>();
((Graphic)component2).color = Color.white;
((Graphic)component2).raycastTarget = true;
button = val3.GetComponent<Button>();
((Selectable)button).targetGraphic = (Graphic)(object)component2;
ColorBlock colors = ((Selectable)button).colors;
((ColorBlock)(ref colors)).normalColor = new Color(0.05f, 0.09f, 0.12f, 1f);
((ColorBlock)(ref colors)).highlightedColor = new Color(0.16f, 0.25f, 0.3f, 1f);
((ColorBlock)(ref colors)).pressedColor = new Color(0.1f, 0.35f, 0.4f, 1f);
((ColorBlock)(ref colors)).disabledColor = new Color(0.045f, 0.06f, 0.07f, 1f);
((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).normalColor;
((Selectable)button).colors = colors;
GameObject val5 = new GameObject("Label", new Type[2]
{
typeof(RectTransform),
typeof(Text)
});
val5.transform.SetParent(val3.transform, false);
Text component3 = val5.GetComponent<Text>();
component3.text = "Complete Replication Queue";
component3.font = window.multiValueText.font;
component3.fontSize = 16;
component3.alignment = (TextAnchor)4;
((Graphic)component3).color = Color.white;
((Graphic)component3).raycastTarget = false;
RectTransform rectTransform = ((Graphic)component3).rectTransform;
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
rectTransform.offsetMin = offsetMin;
UIButton val6 = val3.AddComponent<UIButton>();
val6.button = button;
val6.tips = new TipSettings
{
tipTitle = "Complete Replication Queue",
tipText = "Immediately complete the current queue using its reserved ingredients. Very large queues may briefly pause the game.",
width = 290,
delay = 0.35f,
corner = 3
};
ButtonClickedEvent onClick = button.onClick;
if (val == null)
{
val = (UnityAction)delegate
{
complete(Forge(owner));
};
}
((UnityEvent)onClick).AddListener(val);
}
Refresh(window);
}
internal void Refresh(UIReplicatorWindow window)
{
if (!((Object)(object)button == (Object)null) && !((Object)(object)owner != (Object)(object)window))
{
MechaForge val = Forge(window);
((Selectable)button).interactable = val != null && val.tasks != null && val.tasks.Count > 0 && available(val);
}
}
public void Dispose()
{
if ((Object)(object)button != (Object)null)
{
((UnityEventBase)button.onClick).RemoveAllListeners();
((Component)button).gameObject.SetActive(false);
Object.Destroy((Object)(object)((Component)button).gameObject);
}
button = null;
owner = null;
}
}