using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using xiaoye97;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DSPCustomRecipes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DSPCustomRecipes")]
[assembly: AssemblyTitle("DSPCustomRecipes")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DSPCustomRecipes
{
public static class DefaultConfig
{
public const string Text = "{\n // ===== 自定义配方配置 =====\n // 修改后需要重启游戏才会生效。\n // 物品可以填中文名(如 \"铁矿\")、英文名(如 \"Iron Ore\",会自动对照)或物品 ID(如 1001)。\n // 一条配方里:\n // name 显示名称(可选)\n // description 说明文字(可选)\n // base 以某个现有配方为模板(配方 ID 或名称),下面写了的字段会覆盖模板内容\n // type 配方类型:Smelt(冶炼) / Chemical(化工) / Refine(精炼) / Assemble(制造)\n // / Particle(粒子) / Fractionate(分馏) / Research(研究)\n // time 单次生产耗时(秒)\n // inputs 输入物品\n // outputs 产出物品\n // handcraft 是否显示在复制台(手搓)里,默认 false\n // unlock always(默认,直接解锁) / none(不自动解锁) / tech:<科技名称或ID>\n // icon 自定义图标所用物品(可选,默认使用产出物品的图标)\n // gridIndex 手动指定图标格子(可选,填 页*1000+行*100+列,例如 2101)\n\n \"recipes\": [\n {\n // 示例 1:完全新增——电弧熔炉用 1 个铁矿直接熔出 2 个铁块\n \"name\": \"高效冶炼铁块\",\n \"description\": \"铁矿 x1 -> 铁块 x2\",\n \"type\": \"Smelt\",\n \"time\": 1,\n \"inputs\": [ { \"item\": \"铁矿\", \"count\": 1 } ],\n \"outputs\": [ { \"item\": \"铁块\", \"count\": 2 } ],\n \"handcraft\": false\n },\n {\n // 示例 2:以现有配方 \"铁块\" 为模板,只改耗时并允许手搓\n \"name\": \"铁块(快速手搓版)\",\n \"description\": \"以原版铁块配方为模板,耗时缩短到 0.5 秒\",\n \"base\": \"铁块\",\n \"time\": 0.5,\n \"inputs\": [ { \"item\": \"铁矿\", \"count\": 2 } ],\n \"outputs\": [ { \"item\": \"铁块\", \"count\": 1 } ],\n \"handcraft\": true\n }\n ]\n}\n";
}
public enum JsonKind
{
Null,
Bool,
Number,
String,
Array,
Object
}
public sealed class JsonValue
{
private sealed class Parser
{
private readonly string _text;
private int _pos;
public int Position => _pos;
public bool AtEnd => _pos >= _text.Length;
public Parser(string text)
{
_text = text ?? string.Empty;
}
public void SkipTrivia()
{
while (!AtEnd)
{
char c = _text[_pos];
if (char.IsWhiteSpace(c))
{
_pos++;
continue;
}
if (c != '/' || _pos + 1 >= _text.Length)
{
break;
}
switch (_text[_pos + 1])
{
case '/':
_pos += 2;
while (!AtEnd && _text[_pos] != '\n')
{
_pos++;
}
break;
case '*':
_pos += 2;
while (_pos + 1 < _text.Length && (_text[_pos] != '*' || _text[_pos + 1] != '/'))
{
_pos++;
}
_pos = Math.Min(_pos + 2, _text.Length);
break;
default:
return;
}
}
}
public JsonValue ParseValue()
{
SkipTrivia();
if (AtEnd)
{
throw new FormatException("JSON 意外结束。");
}
switch (_text[_pos])
{
case '{':
return ParseObject();
case '[':
return ParseArray();
case '"':
return new JsonValue
{
Kind = JsonKind.String,
StringValue = ParseString()
};
case 't':
Expect("true");
return new JsonValue
{
Kind = JsonKind.Bool,
BoolValue = true
};
case 'f':
Expect("false");
return new JsonValue
{
Kind = JsonKind.Bool,
BoolValue = false
};
case 'n':
Expect("null");
return new JsonValue
{
Kind = JsonKind.Null
};
default:
return ParseNumber();
}
}
private JsonValue ParseObject()
{
Dictionary<string, JsonValue> dictionary = new Dictionary<string, JsonValue>(StringComparer.OrdinalIgnoreCase);
_pos++;
SkipTrivia();
if (!AtEnd && _text[_pos] == '}')
{
_pos++;
return new JsonValue
{
Kind = JsonKind.Object,
ObjectValue = dictionary
};
}
while (true)
{
SkipTrivia();
if (AtEnd || _text[_pos] != '"')
{
throw new FormatException("JSON 对象的键必须是字符串(位置 " + _pos + ")。");
}
string key = ParseString();
SkipTrivia();
if (AtEnd || _text[_pos] != ':')
{
throw new FormatException("JSON 对象缺少冒号(位置 " + _pos + ")。");
}
_pos++;
dictionary[key] = ParseValue();
SkipTrivia();
if (AtEnd)
{
throw new FormatException("JSON 对象未闭合。");
}
if (_text[_pos] != ',')
{
break;
}
_pos++;
}
if (_text[_pos] == '}')
{
_pos++;
return new JsonValue
{
Kind = JsonKind.Object,
ObjectValue = dictionary
};
}
throw new FormatException("JSON 对象缺少逗号或右花括号(位置 " + _pos + ")。");
}
private JsonValue ParseArray()
{
List<JsonValue> list = new List<JsonValue>();
_pos++;
SkipTrivia();
if (!AtEnd && _text[_pos] == ']')
{
_pos++;
return new JsonValue
{
Kind = JsonKind.Array,
ArrayValue = list
};
}
while (true)
{
list.Add(ParseValue());
SkipTrivia();
if (AtEnd)
{
throw new FormatException("JSON 数组未闭合。");
}
if (_text[_pos] != ',')
{
break;
}
_pos++;
}
if (_text[_pos] == ']')
{
_pos++;
return new JsonValue
{
Kind = JsonKind.Array,
ArrayValue = list
};
}
throw new FormatException("JSON 数组缺少逗号或右方括号(位置 " + _pos + ")。");
}
private string ParseString()
{
StringBuilder stringBuilder = new StringBuilder();
_pos++;
while (!AtEnd)
{
char c = _text[_pos++];
switch (c)
{
case '"':
return stringBuilder.ToString();
default:
stringBuilder.Append(c);
break;
case '\\':
{
if (AtEnd)
{
throw new FormatException("JSON 字符串转义不完整。");
}
char c2 = _text[_pos++];
switch (c2)
{
case '"':
stringBuilder.Append('"');
break;
case '\\':
stringBuilder.Append('\\');
break;
case '/':
stringBuilder.Append('/');
break;
case 'b':
stringBuilder.Append('\b');
break;
case 'f':
stringBuilder.Append('\f');
break;
case 'n':
stringBuilder.Append('\n');
break;
case 'r':
stringBuilder.Append('\r');
break;
case 't':
stringBuilder.Append('\t');
break;
case 'u':
if (_pos + 4 > _text.Length)
{
throw new FormatException("JSON \\u 转义不完整。");
}
stringBuilder.Append((char)Convert.ToInt32(_text.Substring(_pos, 4), 16));
_pos += 4;
break;
default:
throw new FormatException("无法识别的 JSON 转义:" + c2);
}
break;
}
}
}
throw new FormatException("JSON 字符串未闭合。");
}
private JsonValue ParseNumber()
{
int pos = _pos;
while (!AtEnd)
{
char c = _text[_pos];
if ((c < '0' || c > '9') && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E')
{
break;
}
_pos++;
}
string text = _text.Substring(pos, _pos - pos);
if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
throw new FormatException("无法识别的 JSON 数字:" + text);
}
return new JsonValue
{
Kind = JsonKind.Number,
NumberValue = result
};
}
private void Expect(string literal)
{
if (_pos + literal.Length > _text.Length || string.CompareOrdinal(_text, _pos, literal, 0, literal.Length) != 0)
{
throw new FormatException("JSON 关键字无效(位置 " + _pos + ")。");
}
_pos += literal.Length;
}
}
public JsonKind Kind;
public bool BoolValue;
public double NumberValue;
public string StringValue;
public List<JsonValue> ArrayValue;
public Dictionary<string, JsonValue> ObjectValue;
public bool IsNull => Kind == JsonKind.Null;
public JsonValue Get(string key)
{
if (Kind != JsonKind.Object || ObjectValue == null)
{
return null;
}
if (!ObjectValue.TryGetValue(key, out var value))
{
return null;
}
return value;
}
public string AsString(string fallback = null)
{
if (Kind == JsonKind.String)
{
return StringValue;
}
if (Kind == JsonKind.Number)
{
return NumberValue.ToString(CultureInfo.InvariantCulture);
}
if (Kind == JsonKind.Bool)
{
if (!BoolValue)
{
return "false";
}
return "true";
}
return fallback;
}
public double? AsNumber()
{
if (Kind == JsonKind.Number)
{
return NumberValue;
}
if (Kind == JsonKind.String && double.TryParse(StringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
return result;
}
return null;
}
public int? AsInt()
{
double? num = AsNumber();
if (!num.HasValue)
{
return null;
}
return (int)Math.Round(num.Value);
}
public bool? AsBool()
{
if (Kind == JsonKind.Bool)
{
return BoolValue;
}
if (Kind == JsonKind.Number)
{
return NumberValue != 0.0;
}
if (Kind == JsonKind.String)
{
switch (StringValue.Trim().ToLowerInvariant())
{
case "true":
case "1":
case "yes":
case "是":
return true;
case "false":
case "0":
case "no":
case "否":
return false;
}
}
return null;
}
public List<JsonValue> AsArray()
{
if (Kind != JsonKind.Array)
{
return null;
}
return ArrayValue;
}
public static JsonValue Parse(string text)
{
Parser parser = new Parser(text);
JsonValue result = parser.ParseValue();
parser.SkipTrivia();
if (!parser.AtEnd)
{
throw new FormatException("JSON 末尾存在多余内容(位置 " + parser.Position + ")。");
}
return result;
}
}
[BepInPlugin("dsp.customrecipes", "DSP Custom Recipes", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class CustomRecipesPlugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(GameHistoryData), "Import")]
private static class HistoryImportPatch
{
private static void Postfix(GameHistoryData __instance)
{
UnlockAll(__instance, "读取存档");
}
}
[HarmonyPatch(typeof(GameHistoryData), "SetForNewGame")]
private static class HistoryNewGamePatch
{
private static void Postfix(GameHistoryData __instance)
{
UnlockAll(__instance, "新游戏");
}
}
private static class Lookup
{
private static readonly Dictionary<string, ItemProto> Items = new Dictionary<string, ItemProto>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, RecipeProto> Recipes = new Dictionary<string, RecipeProto>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, TechProto> Techs = new Dictionary<string, TechProto>(StringComparer.OrdinalIgnoreCase);
private static bool _itemsBuilt;
private static bool _recipesBuilt;
private static bool _techsBuilt;
private static Dictionary<string, string> _englishAliases;
private static bool _englishAliasesLoaded;
public static ItemProto FindItem(string spec)
{
return Find<ItemProto>(spec, (Action)EnsureItems, Items, ((ProtoSet<ItemProto>)(object)LDB.items).dataArray, (Func<int, ItemProto>)((int id) => ((ProtoSet<ItemProto>)(object)LDB.items).Select(id)));
}
public static RecipeProto FindRecipe(string spec)
{
return Find<RecipeProto>(spec, (Action)EnsureRecipes, Recipes, ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray, (Func<int, RecipeProto>)((int id) => ((ProtoSet<RecipeProto>)(object)LDB.recipes).Select(id)));
}
public static TechProto FindTech(string spec)
{
return Find<TechProto>(spec, (Action)EnsureTechs, Techs, ((ProtoSet<TechProto>)(object)LDB.techs).dataArray, (Func<int, TechProto>)((int id) => ((ProtoSet<TechProto>)(object)LDB.techs).Select(id)));
}
private static T Find<T>(string spec, Action ensure, Dictionary<string, T> map, T[] dataArray, Func<int, T> selectById) where T : Proto
{
if (string.IsNullOrEmpty(spec))
{
return default(T);
}
spec = spec.Trim();
if (int.TryParse(spec, out var result))
{
T val = selectById(result);
if (val != null)
{
return val;
}
}
ensure();
if (map.TryGetValue(spec, out var value))
{
return value;
}
string text = ResolveEnglishAlias(spec);
if (!string.IsNullOrEmpty(text) && map.TryGetValue(text, out var value2))
{
return value2;
}
string text2 = (string.IsNullOrEmpty(text) ? spec : text);
if (text2.Length < 2)
{
return default(T);
}
foreach (T val2 in dataArray)
{
if (val2 != null && !string.IsNullOrEmpty(((Proto)val2).Name))
{
if (((Proto)val2).Name.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
{
return val2;
}
string text3 = Localization.Translate(((Proto)val2).Name);
if (!string.IsNullOrEmpty(text3) && text3.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
{
return val2;
}
}
}
return default(T);
}
private static void EnsureItems()
{
if (_itemsBuilt)
{
return;
}
_itemsBuilt = true;
ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
foreach (ItemProto val in dataArray)
{
if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
{
TryAdd(Items, ((Proto)val).Name, val);
string text = Localization.Translate(((Proto)val).Name);
if (!string.IsNullOrEmpty(text))
{
TryAdd(Items, text, val);
}
if (!string.IsNullOrEmpty(((Proto)val).SID))
{
TryAdd(Items, ((Proto)val).SID, val);
}
}
}
}
private static void EnsureRecipes()
{
if (_recipesBuilt)
{
return;
}
_recipesBuilt = true;
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
{
TryAdd(Recipes, ((Proto)val).Name, val);
string text = Localization.Translate(((Proto)val).Name);
if (!string.IsNullOrEmpty(text))
{
TryAdd(Recipes, text, val);
}
}
}
}
private static void EnsureTechs()
{
if (_techsBuilt)
{
return;
}
_techsBuilt = true;
TechProto[] dataArray = ((ProtoSet<TechProto>)(object)LDB.techs).dataArray;
foreach (TechProto val in dataArray)
{
if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
{
TryAdd(Techs, ((Proto)val).Name, val);
string text = Localization.Translate(((Proto)val).Name);
if (!string.IsNullOrEmpty(text))
{
TryAdd(Techs, text, val);
}
}
}
}
private static string ResolveEnglishAlias(string spec)
{
EnsureEnglishAliases();
if (_englishAliases != null && _englishAliases.TryGetValue(spec, out var value))
{
return value;
}
return null;
}
private static void EnsureEnglishAliases()
{
if (_englishAliasesLoaded)
{
return;
}
_englishAliasesLoaded = true;
try
{
string directoryName = Path.GetDirectoryName(Application.dataPath);
if (string.IsNullOrEmpty(directoryName))
{
return;
}
string path = Path.Combine(directoryName, "Locale", "1033", "prototype.txt");
if (!File.Exists(path))
{
return;
}
Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string[] array = File.ReadAllLines(path, Encoding.Unicode);
foreach (string text in array)
{
if (string.IsNullOrEmpty(text) || text[0] == '[' || text[0] == '/')
{
continue;
}
string[] array2 = text.Split('\t');
if (array2.Length >= 4)
{
string text2 = array2[0].Trim();
string text3 = array2[3].Trim();
if (text2.Length != 0 && text3.Length != 0 && !dictionary.ContainsKey(text3))
{
dictionary[text3] = text2;
}
}
}
_englishAliases = dictionary;
Log.LogInfo((object)("已载入英文名称对照表,共 " + dictionary.Count + " 条。"));
}
catch (Exception ex)
{
Log.LogWarning((object)("载入英文名称对照表失败(不影响中文名与物品 ID):" + ex.Message));
}
}
private static void TryAdd<T>(Dictionary<string, T> map, string key, T value)
{
if (!string.IsNullOrEmpty(key) && !map.ContainsKey(key))
{
map[key] = value;
}
}
}
public const string ModGuid = "dsp.customrecipes";
public const string ModName = "DSP Custom Recipes";
public const string ModVersion = "1.0.0";
public const string LDBToolGuid = "me.xiaoye97.plugin.Dyson.LDBTool";
internal static ManualLogSource Log;
internal static CustomRecipesPlugin Instance;
private static readonly List<RecipeProto> AddedRecipes = new List<RecipeProto>();
private static readonly Dictionary<RecipeProto, RecipeDefinition> RecipeDefinitions = new Dictionary<RecipeProto, RecipeDefinition>();
private static readonly List<RecipeDefinition> Definitions = new List<RecipeDefinition>();
private Harmony _harmony;
internal static bool SetupFinished;
internal static int AppliedCount;
private static bool _warnedGridOverflow;
private static FieldInfo _ldbToolFinishedField;
private static MethodInfo _findRecipesMethod;
private static MethodInfo _unlockRecipeMethod;
internal static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "dsp.customrecipes");
internal static string ConfigFile_ => Path.Combine(ConfigDirectory, "recipes.json");
private static bool LDBToolFinished
{
get
{
try
{
if (_ldbToolFinishedField == null)
{
_ldbToolFinishedField = AccessTools.Field(typeof(LDBTool), "Finshed");
}
return _ldbToolFinishedField != null && (bool)_ldbToolFinishedField.GetValue(null);
}
catch
{
return false;
}
}
}
private void Awake()
{
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
LoadDefinitions();
if (Definitions.Count > 0)
{
LDBTool.PostAddDataAction = (Action)Delegate.Combine(LDBTool.PostAddDataAction, new Action(OnPostAddData));
}
_harmony = new Harmony("dsp.customrecipes");
try
{
_harmony.PatchAll(typeof(CustomRecipesPlugin).Assembly);
}
catch (Exception ex)
{
Log.LogError((object)("Harmony 补丁失败:" + ex));
}
PatchVFPreload();
}
private void PatchVFPreload()
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Expected O, but got Unknown
try
{
Type type = AccessTools.TypeByName("VFPreload");
if (type == null)
{
Log.LogError((object)"找不到 VFPreload 类型,无法挂接加载完成回调,自定义配方可能不会生效。");
return;
}
MethodInfo methodInfo = AccessTools.Method(type, "InvokeOnLoadWorkEnded", (Type[])null, (Type[])null);
MethodInfo method = typeof(CustomRecipesPlugin).GetMethod("OnLoadWorkEndedPostfix", BindingFlags.Static | BindingFlags.NonPublic);
_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
catch (Exception ex)
{
Log.LogError((object)("挂接 VFPreload 失败:" + ex));
}
}
private static void OnLoadWorkEndedPostfix()
{
if ((Object)(object)Instance != (Object)null)
{
((MonoBehaviour)Instance).StartCoroutine(Instance.FinishSetupRoutine());
}
}
private void LoadDefinitions()
{
try
{
if (!Directory.Exists(ConfigDirectory))
{
Directory.CreateDirectory(ConfigDirectory);
}
if (!File.Exists(ConfigFile_))
{
File.WriteAllText(ConfigFile_, "{\n // ===== 自定义配方配置 =====\n // 修改后需要重启游戏才会生效。\n // 物品可以填中文名(如 \"铁矿\")、英文名(如 \"Iron Ore\",会自动对照)或物品 ID(如 1001)。\n // 一条配方里:\n // name 显示名称(可选)\n // description 说明文字(可选)\n // base 以某个现有配方为模板(配方 ID 或名称),下面写了的字段会覆盖模板内容\n // type 配方类型:Smelt(冶炼) / Chemical(化工) / Refine(精炼) / Assemble(制造)\n // / Particle(粒子) / Fractionate(分馏) / Research(研究)\n // time 单次生产耗时(秒)\n // inputs 输入物品\n // outputs 产出物品\n // handcraft 是否显示在复制台(手搓)里,默认 false\n // unlock always(默认,直接解锁) / none(不自动解锁) / tech:<科技名称或ID>\n // icon 自定义图标所用物品(可选,默认使用产出物品的图标)\n // gridIndex 手动指定图标格子(可选,填 页*1000+行*100+列,例如 2101)\n\n \"recipes\": [\n {\n // 示例 1:完全新增——电弧熔炉用 1 个铁矿直接熔出 2 个铁块\n \"name\": \"高效冶炼铁块\",\n \"description\": \"铁矿 x1 -> 铁块 x2\",\n \"type\": \"Smelt\",\n \"time\": 1,\n \"inputs\": [ { \"item\": \"铁矿\", \"count\": 1 } ],\n \"outputs\": [ { \"item\": \"铁块\", \"count\": 2 } ],\n \"handcraft\": false\n },\n {\n // 示例 2:以现有配方 \"铁块\" 为模板,只改耗时并允许手搓\n \"name\": \"铁块(快速手搓版)\",\n \"description\": \"以原版铁块配方为模板,耗时缩短到 0.5 秒\",\n \"base\": \"铁块\",\n \"time\": 0.5,\n \"inputs\": [ { \"item\": \"铁矿\", \"count\": 2 } ],\n \"outputs\": [ { \"item\": \"铁块\", \"count\": 1 } ],\n \"handcraft\": true\n }\n ]\n}\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
Log.LogInfo((object)("已生成默认配置文件:" + ConfigFile_));
}
JsonValue root = JsonValue.Parse(File.ReadAllText(ConfigFile_, Encoding.UTF8));
Definitions.AddRange(RecipeDefinition.ParseAll(root));
Log.LogInfo((object)("读取到 " + Definitions.Count + " 条自定义配方定义:" + ConfigFile_));
}
catch (Exception ex)
{
Log.LogError((object)("读取配置文件失败,本次不会添加任何配方:" + ex));
}
}
private static void OnPostAddData()
{
RegisterAllRecipes(null);
}
private static HashSet<int> CollectUsedGridIndexes()
{
HashSet<int> hashSet = new HashSet<int>();
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val != null && val.GridIndex > 0)
{
hashSet.Add(val.GridIndex);
}
}
foreach (RecipeProto item in EnumeratePendingProtos<RecipeProto>())
{
if (item != null && item.GridIndex > 0)
{
hashSet.Add(item.GridIndex);
}
}
return hashSet;
}
private static IEnumerable<T> EnumeratePendingProtos<T>() where T : Proto
{
string[] array = new string[2] { "PostToAdd", "PreToAdd" };
foreach (string text in array)
{
FieldInfo fieldInfo = null;
try
{
fieldInfo = AccessTools.Field(typeof(LDBTool), text);
}
catch
{
}
if (fieldInfo == null)
{
continue;
}
object obj2 = null;
try
{
obj2 = fieldInfo.GetValue(null);
}
catch
{
}
if (!(obj2 is IEnumerable enumerable))
{
continue;
}
foreach (object item in enumerable)
{
if (!(item is IEnumerable enumerable2))
{
continue;
}
foreach (object item2 in enumerable2)
{
T val = (T)((item2 is T) ? item2 : null);
if (val != null)
{
yield return val;
}
}
}
}
}
private static HashSet<int> CollectUsedRecipeIds()
{
HashSet<int> hashSet = new HashSet<int>();
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val != null)
{
hashSet.Add(((Proto)val).ID);
}
}
foreach (RecipeProto item in EnumeratePendingProtos<RecipeProto>())
{
hashSet.Add(((Proto)item).ID);
}
return hashSet;
}
private static void RegisterAllRecipes(HashSet<int> ids)
{
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
try
{
if (Definitions.Count == 0)
{
return;
}
if (ids == null)
{
ids = CollectUsedRecipeIds();
}
HashSet<int> usedGrids = CollectUsedGridIndexes();
int nextId = ((ids.Count <= 0) ? 1 : (ids.Max() + 1));
HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
{
hashSet.Add(((Proto)val).Name);
}
}
foreach (RecipeDefinition definition in Definitions)
{
try
{
RecipeProto val2 = BuildRecipe(definition, ids, usedGrids, hashSet, ref nextId);
if (val2 != null)
{
LDBTool.PostAddProto((Proto)(object)val2);
AddedRecipes.Add(val2);
RecipeDefinitions[val2] = definition;
Log.LogInfo((object)$"已排队注入自定义配方 [{((Proto)val2).Name}] ID={((Proto)val2).ID} 类型={val2.Type} 耗时={(float)val2.TimeSpend / 60f:0.##}s 输入={DescribeItems(val2.Items, val2.ItemCounts)} 产出={DescribeItems(val2.Results, val2.ResultCounts)}");
}
}
catch (Exception arg)
{
Log.LogError((object)$"创建第 {definition.Index} 条自定义配方失败:{arg}");
}
}
}
catch (Exception ex)
{
Log.LogError((object)("注入自定义配方时发生异常:" + ex));
}
}
private static RecipeProto BuildRecipe(RecipeDefinition def, HashSet<int> ids, HashSet<int> usedGrids, HashSet<string> usedNames, ref int nextId)
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: 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_0174: Unknown result type (might be due to invalid IL or missing references)
//IL_0176: Invalid comparison between Unknown and I4
//IL_0351: Unknown result type (might be due to invalid IL or missing references)
//IL_0356: Unknown result type (might be due to invalid IL or missing references)
//IL_035e: Unknown result type (might be due to invalid IL or missing references)
//IL_0366: Unknown result type (might be due to invalid IL or missing references)
//IL_036e: Unknown result type (might be due to invalid IL or missing references)
//IL_036f: Unknown result type (might be due to invalid IL or missing references)
//IL_0370: Unknown result type (might be due to invalid IL or missing references)
//IL_0375: Unknown result type (might be due to invalid IL or missing references)
//IL_037d: Unknown result type (might be due to invalid IL or missing references)
//IL_0389: Unknown result type (might be due to invalid IL or missing references)
//IL_0395: Unknown result type (might be due to invalid IL or missing references)
//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
//IL_03af: Unknown result type (might be due to invalid IL or missing references)
//IL_03db: Unknown result type (might be due to invalid IL or missing references)
//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0407: Unknown result type (might be due to invalid IL or missing references)
//IL_0410: Expected O, but got Unknown
RecipeProto val = null;
if (!string.IsNullOrEmpty(def.BaseSpec))
{
val = Lookup.FindRecipe(def.BaseSpec);
if (val == null)
{
Log.LogWarning((object)$"配方 [{def.Name}] 的 base=\"{def.BaseSpec}\" 找不到,将只使用自定义内容。");
}
}
ERecipeType type = (ERecipeType)4;
if (!string.IsNullOrEmpty(def.TypeName))
{
if (!TryParseRecipeType(def.TypeName, out type))
{
type = (ERecipeType)((val == null) ? 4 : ((int)val.Type));
Log.LogWarning((object)$"配方 [{def.Name}] 的类型 \"{def.TypeName}\" 无法识别,使用 {type}。");
}
}
else if (val != null)
{
type = val.Type;
}
List<int> list = new List<int>();
List<int> list2 = new List<int>();
List<int> list3 = new List<int>();
List<int> list4 = new List<int>();
if (def.Inputs.Count == 0 && val != null)
{
list.AddRange(val.Items);
list2.AddRange(val.ItemCounts);
}
else if (!ResolveIngredients(def, def.Inputs, list, list2, "输入"))
{
return null;
}
if (def.Outputs.Count == 0 && val != null)
{
list3.AddRange(val.Results);
list4.AddRange(val.ResultCounts);
}
else if (!ResolveIngredients(def, def.Outputs, list3, list4, "产出"))
{
return null;
}
if (list.Count == 0 || list3.Count == 0)
{
Log.LogError((object)$"配方 [{def.Name}] 缺少输入或产出,已跳过。");
return null;
}
if ((int)type == 8 && (list.Count != 1 || list3.Count != 1))
{
Log.LogError((object)$"配方 [{def.Name}] 是分馏配方,输入与产出都只能有 1 种物品,已跳过。");
return null;
}
float num = def.TimeSeconds ?? ((val != null) ? ((float)val.TimeSpend / 60f) : 1f);
int timeSpend = Mathf.Max(1, Mathf.RoundToInt(num * 60f));
string text = def.Name;
if (string.IsNullOrEmpty(text))
{
text = ((val != null) ? (((Proto)val).Name + " (自定义)") : ("自定义配方 " + def.Index));
}
string text2 = text;
int num2 = 2;
while (usedNames.Contains(text2))
{
text2 = text + " #" + num2;
num2++;
}
usedNames.Add(text2);
string text3 = def.Description;
if (string.IsNullOrEmpty(text3))
{
text3 = $"自定义配方:{DescribeItems(list, list2)} → {DescribeItems(list3, list4)}";
}
TechProto val2 = null;
if (def.Unlock == UnlockMode.Tech)
{
val2 = Lookup.FindTech(def.UnlockTech);
if (val2 == null)
{
Log.LogWarning((object)$"配方 [{text2}] 指定的解锁科技 \"{def.UnlockTech}\" 找不到,改为直接解锁。");
}
}
int num3;
if (def.GridIndex.HasValue && def.GridIndex.Value > 0)
{
num3 = def.GridIndex.Value;
if (num3 < 1101)
{
Log.LogWarning((object)$"配方 [{text2}] 的 gridIndex={num3} 小于 1101,配方可能不会显示在界面中。");
}
usedGrids.Add(num3);
}
else
{
num3 = AllocateGridIndex(usedGrids);
}
int i;
for (i = nextId; ids.Contains(i); i++)
{
}
nextId = i + 1;
ids.Add(i);
return new RecipeProto
{
ID = i,
Name = text2,
Description = text3,
Type = type,
TimeSpend = timeSpend,
Items = list.ToArray(),
ItemCounts = list2.ToArray(),
Results = list3.ToArray(),
ResultCounts = list4.ToArray(),
Handcraft = (def.Handcraft ?? val?.Handcraft ?? false),
Explicit = (def.Explicit ?? true),
NonProductive = false,
IconPath = string.Empty,
GridIndex = num3,
preTech = val2
};
}
private static bool ResolveIngredients(RecipeDefinition def, List<IngredientSpec> specs, List<int> itemIds, List<int> counts, string label)
{
foreach (IngredientSpec spec in specs)
{
ItemProto val = Lookup.FindItem(spec.Item);
if (val == null)
{
Log.LogError((object)$"配方 [{def.Name}] 的{label}物品 \"{spec.Item}\" 找不到,已跳过该配方。");
return false;
}
if (spec.Count <= 0)
{
Log.LogError((object)$"配方 [{def.Name}] 的{label}物品 \"{spec.Item}\" 数量必须大于 0,已跳过该配方。");
return false;
}
itemIds.Add(((Proto)val).ID);
counts.Add(spec.Count);
}
return true;
}
private static int AllocateGridIndex(HashSet<int> used)
{
for (int i = 1; i <= 2; i++)
{
for (int j = 1; j <= 8; j++)
{
for (int k = 1; k <= 12; k++)
{
int num = i * 1000 + j * 100 + k;
if (!used.Contains(num))
{
used.Add(num);
return num;
}
}
}
}
for (int l = 3; l <= 9; l++)
{
for (int m = 1; m <= 8; m++)
{
for (int n = 1; n <= 12; n++)
{
int num2 = l * 1000 + m * 100 + n;
if (!used.Contains(num2))
{
used.Add(num2);
if (!_warnedGridOverflow)
{
_warnedGridOverflow = true;
Log.LogWarning((object)"配方图标格子前两页已占满,新配方被放到第 3 页以后,可能不会显示在配方选择器中,建议手动指定 gridIndex。");
}
return num2;
}
}
}
}
int num3;
for (num3 = 91001; used.Contains(num3); num3++)
{
}
used.Add(num3);
return num3;
}
private static string DescribeItems(IList<int> ids, IList<int> counts)
{
if (ids == null || counts == null || ids.Count == 0)
{
return "无";
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < ids.Count; i++)
{
if (i > 0)
{
stringBuilder.Append(" + ");
}
ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(ids[i]);
string value = ((val != null) ? Localization.Translate(((Proto)val).Name) : ("#" + ids[i]));
stringBuilder.Append(value);
if (i < counts.Count && counts[i] != 1)
{
stringBuilder.Append(" x").Append(counts[i]);
}
}
return stringBuilder.ToString();
}
internal static bool TryParseRecipeType(string raw, out ERecipeType type)
{
//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
//IL_04bf: Expected I4, but got Unknown
type = (ERecipeType)4;
if (string.IsNullOrEmpty(raw))
{
return false;
}
string text = raw.Trim();
switch (text.ToLowerInvariant())
{
case "none":
type = (ERecipeType)0;
return true;
case "smelting":
case "熔炼":
case "冶炼":
case "smelt":
type = (ERecipeType)1;
return true;
case "chemical":
case "化工":
case "化工厂":
type = (ERecipeType)2;
return true;
case "refining":
case "精炼":
case "精炼厂":
case "refine":
type = (ERecipeType)3;
return true;
case "assemble":
case "组装":
case "制造":
case "制造台":
case "assembler":
type = (ERecipeType)4;
return true;
case "particle":
case "粒子":
case "对撞":
type = (ERecipeType)5;
return true;
case "exchange":
case "交换":
type = (ERecipeType)6;
return true;
case "光子":
case "photon":
case "photonstore":
type = (ERecipeType)7;
return true;
case "fraction":
case "分馏":
case "分馏塔":
case "fractionate":
type = (ERecipeType)8;
return true;
case "research":
case "研究":
case "研究站":
type = (ERecipeType)15;
return true;
default:
{
if (Enum.TryParse<ERecipeType>(text, ignoreCase: true, out ERecipeType result))
{
type = (ERecipeType)(int)result;
return true;
}
return false;
}
}
}
private IEnumerator FinishSetupRoutine()
{
if (SetupFinished)
{
yield break;
}
float waited = 0f;
while (!LDBToolFinished && waited < 20f)
{
waited += Time.unscaledDeltaTime;
yield return null;
}
yield return null;
if (!LDBToolFinished)
{
Log.LogWarning((object)"LDBTool 未在预期时间内完成注入,仍尝试继续。");
}
if (AddedRecipes.Count == 0 && Definitions.Count > 0)
{
Log.LogWarning((object)"自定义配方没有在 LDB 加载阶段注册,尝试补救注入。");
TryFallbackRegister();
}
if (AddedRecipes.Count == 0)
{
Log.LogWarning((object)"没有需要添加的自定义配方。");
SetupFinished = true;
yield break;
}
try
{
AppliedCount = ApplyRecipes();
RebuildItemRecipeCaches();
RecreateIcons();
SetupFinished = true;
Log.LogInfo((object)$"自定义配方初始化完成:{AppliedCount}/{AddedRecipes.Count}。");
if (GameMain.history != null)
{
UnlockAll(GameMain.history, "初始化");
}
}
catch (Exception ex)
{
Log.LogError((object)("初始化自定义配方失败:" + ex));
}
}
private static void TryFallbackRegister()
{
try
{
RegisterAllRecipes(CollectUsedRecipeIds());
if (AddedRecipes.Count == 0)
{
return;
}
MethodInfo method = typeof(LDBTool).GetMethod("AddProtosToSet", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (method == null)
{
Log.LogError((object)"补救注入失败:找不到 LDBTool.AddProtosToSet。");
return;
}
List<Proto> list = new List<Proto>();
foreach (RecipeProto addedRecipe in AddedRecipes)
{
list.Add((Proto)(object)addedRecipe);
}
method.MakeGenericMethod(typeof(RecipeProto)).Invoke(null, new object[2]
{
LDB.recipes,
list
});
Log.LogWarning((object)("已使用补救方式注入 " + list.Count + " 条自定义配方。"));
}
catch (Exception ex)
{
Log.LogError((object)("补救注入失败:" + ex));
}
}
private static int ApplyRecipes()
{
int num = 0;
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto addedRecipe in AddedRecipes)
{
try
{
int num2 = Array.IndexOf(dataArray, addedRecipe);
if (num2 < 0)
{
Log.LogError((object)$"配方 [{((Proto)addedRecipe).Name}] 没有出现在 LDB 中,可能与其它模组冲突,已跳过。");
continue;
}
addedRecipe.Preload(num2);
num++;
if (RecipeDefinitions.TryGetValue(addedRecipe, out var value) && !string.IsNullOrEmpty(value.IconItem))
{
ItemProto val = Lookup.FindItem(value.IconItem);
if (val != null && (Object)(object)val.iconSprite != (Object)null)
{
AccessTools.Field(typeof(RecipeProto), "_iconSprite")?.SetValue(addedRecipe, val.iconSprite);
}
}
}
catch (Exception arg)
{
Log.LogError((object)$"初始化配方 [{((Proto)addedRecipe).Name}] 失败:{arg}");
}
}
return num;
}
private static void RebuildItemRecipeCaches()
{
if (_findRecipesMethod == null)
{
_findRecipesMethod = AccessTools.Method(typeof(ItemProto), "FindRecipes", (Type[])null, (Type[])null);
}
if (_findRecipesMethod == null)
{
Log.LogWarning((object)"找不到 ItemProto.FindRecipes,物品配方列表可能不会包含自定义配方。");
return;
}
ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
foreach (ItemProto val in dataArray)
{
if (val != null)
{
try
{
val.recipes = null;
_findRecipesMethod.Invoke(val, null);
}
catch (Exception ex)
{
Log.LogWarning((object)("重建物品配方缓存失败(" + (((Proto)val).Name ?? "?") + "):" + ex.Message));
}
}
}
}
private static void RecreateIcons()
{
try
{
if (GameMain.iconSet != null)
{
GameMain.iconSet.loaded = false;
GameMain.iconSet.Create();
}
}
catch (Exception ex)
{
Log.LogWarning((object)("重建图标集失败(配方图标可能不显示):" + ex.Message));
}
}
private static void UnlockAll(GameHistoryData history, string source)
{
if (history == null || AddedRecipes.Count == 0)
{
return;
}
int num = 0;
foreach (RecipeProto addedRecipe in AddedRecipes)
{
try
{
RecipeDefinition recipeDefinition = null;
if (RecipeDefinitions.TryGetValue(addedRecipe, out var value))
{
recipeDefinition = value;
}
if ((recipeDefinition == null || recipeDefinition.Unlock != UnlockMode.Never) && (recipeDefinition == null || recipeDefinition.Unlock != UnlockMode.Tech || addedRecipe.preTech == null || history.TechUnlocked(((Proto)addedRecipe.preTech).ID)) && !history.RecipeUnlocked(((Proto)addedRecipe).ID))
{
InvokeUnlockRecipe(history, ((Proto)addedRecipe).ID);
num++;
}
}
catch (Exception ex)
{
Log.LogWarning((object)$"解锁配方 [{((Proto)addedRecipe).Name}] 失败:{ex.Message}");
}
}
if (num > 0)
{
Log.LogInfo((object)$"({source})已解锁 {num} 条自定义配方。");
}
}
private static void InvokeUnlockRecipe(GameHistoryData history, int recipeId)
{
if (_unlockRecipeMethod == null)
{
_unlockRecipeMethod = AccessTools.Method(typeof(GameHistoryData), "UnlockRecipe", new Type[1] { typeof(int) }, (Type[])null);
}
if (_unlockRecipeMethod == null)
{
Log.LogWarning((object)"找不到 GameHistoryData.UnlockRecipe,自定义配方可能不会自动解锁。");
return;
}
_unlockRecipeMethod.Invoke(history, new object[1] { recipeId });
}
}
public enum UnlockMode
{
Always,
Tech,
Never
}
public sealed class IngredientSpec
{
public string Item;
public int Count = 1;
public IngredientSpec()
{
}
public IngredientSpec(string item, int count)
{
Item = item;
Count = count;
}
public override string ToString()
{
return Item + " x" + Count;
}
}
public sealed class RecipeDefinition
{
public int Index;
public string Name;
public string Description;
public string BaseSpec;
public string TypeName;
public float? TimeSeconds;
public List<IngredientSpec> Inputs = new List<IngredientSpec>();
public List<IngredientSpec> Outputs = new List<IngredientSpec>();
public bool? Handcraft;
public UnlockMode Unlock;
public string UnlockTech;
public int? GridIndex;
public string IconItem;
public bool? Explicit;
public static List<RecipeDefinition> ParseAll(JsonValue root)
{
List<RecipeDefinition> list = new List<RecipeDefinition>();
if (root == null || root.IsNull)
{
return list;
}
JsonValue jsonValue = root;
if (root.Kind == JsonKind.Object)
{
jsonValue = root.Get("recipes") ?? root.Get("items") ?? root.Get("list");
}
List<JsonValue> list2 = jsonValue?.AsArray();
if (list2 == null)
{
throw new FormatException("配置文件根节点必须是数组,或者包含 \"recipes\" 数组的对象。");
}
for (int i = 0; i < list2.Count; i++)
{
JsonValue jsonValue2 = list2[i];
if (jsonValue2 != null && jsonValue2.Kind == JsonKind.Object)
{
RecipeDefinition item = ParseOne(jsonValue2, i);
list.Add(item);
}
}
return list;
}
private static RecipeDefinition ParseOne(JsonValue entry, int index)
{
RecipeDefinition recipeDefinition = new RecipeDefinition
{
Index = index + 1
};
recipeDefinition.Name = ReadFirstString(entry, "name", "名称", "displayName");
recipeDefinition.Description = ReadFirstString(entry, "description", "desc", "描述");
recipeDefinition.BaseSpec = ReadFirstString(entry, "base", "from", "copyFrom", "基础配方");
recipeDefinition.TypeName = ReadFirstString(entry, "type", "recipeType", "类型");
recipeDefinition.IconItem = ReadFirstString(entry, "icon", "iconItem", "图标");
recipeDefinition.UnlockTech = ReadFirstString(entry, "tech", "unlockTech", "解锁科技");
JsonValue first = GetFirst(entry, "time", "timeSpend", "seconds", "时间", "耗时");
if (first != null)
{
double? num = first.AsNumber();
if (num.HasValue)
{
recipeDefinition.TimeSeconds = (float)num.Value;
}
}
JsonValue first2 = GetFirst(entry, "gridIndex", "grid", "格子");
if (first2 != null)
{
recipeDefinition.GridIndex = first2.AsInt();
}
JsonValue first3 = GetFirst(entry, "handcraft", "manual", "手搓");
if (first3 != null)
{
recipeDefinition.Handcraft = first3.AsBool();
}
JsonValue first4 = GetFirst(entry, "explicit");
if (first4 != null)
{
recipeDefinition.Explicit = first4.AsBool();
}
recipeDefinition.Inputs = ParseIngredients(GetFirst(entry, "inputs", "input", "in", "输入", "原料"));
recipeDefinition.Outputs = ParseIngredients(GetFirst(entry, "outputs", "output", "out", "results", "产出", "输出", "产物"));
JsonValue first5 = GetFirst(entry, "unlock", "解锁");
if (first5 != null)
{
ApplyUnlock(recipeDefinition, first5.AsString());
}
else if (!string.IsNullOrEmpty(recipeDefinition.UnlockTech))
{
recipeDefinition.Unlock = UnlockMode.Tech;
}
return recipeDefinition;
}
private static void ApplyUnlock(RecipeDefinition def, string raw)
{
if (string.IsNullOrEmpty(raw))
{
return;
}
string text = raw.Trim();
string text2 = text.ToLowerInvariant();
if (!(text2 == "always"))
{
switch (text2)
{
default:
if (text == "总是" || text == "默认")
{
break;
}
switch (text2)
{
default:
if (!(text == "从不") && !(text == "无"))
{
if (text2.StartsWith("tech:"))
{
def.Unlock = UnlockMode.Tech;
def.UnlockTech = text.Substring(5).Trim();
}
else
{
def.Unlock = UnlockMode.Tech;
def.UnlockTech = text;
}
break;
}
goto case "never";
case "never":
case "none":
case "false":
def.Unlock = UnlockMode.Never;
break;
}
return;
case "true":
case "auto":
case "always":
break;
}
}
def.Unlock = UnlockMode.Always;
}
private static List<IngredientSpec> ParseIngredients(JsonValue value)
{
List<IngredientSpec> list = new List<IngredientSpec>();
if (value == null || value.IsNull)
{
return list;
}
List<JsonValue> list2 = value.AsArray();
if (list2 != null)
{
foreach (JsonValue item in list2)
{
if (item != null && !item.IsNull)
{
if (item.Kind == JsonKind.String || item.Kind == JsonKind.Number)
{
list.Add(new IngredientSpec(item.AsString(), 1));
}
else if (item.Kind == JsonKind.Object)
{
string text = ReadFirstString(item, "item", "id", "name", "物品", "物品ID");
int num = 1;
JsonValue first = GetFirst(item, "count", "amount", "num", "数量");
if (first != null)
{
int? num2 = first.AsInt();
if (num2.HasValue)
{
num = num2.Value;
}
}
if (!string.IsNullOrEmpty(text) && num > 0)
{
list.Add(new IngredientSpec(text, num));
}
}
}
}
return list;
}
if (value.Kind == JsonKind.Object)
{
foreach (KeyValuePair<string, JsonValue> item2 in value.ObjectValue)
{
int? num3 = item2.Value.AsInt();
if (num3.HasValue && num3.Value > 0)
{
list.Add(new IngredientSpec(item2.Key, num3.Value));
}
}
}
return list;
}
private static JsonValue GetFirst(JsonValue obj, params string[] keys)
{
foreach (string key in keys)
{
JsonValue jsonValue = obj.Get(key);
if (jsonValue != null)
{
return jsonValue;
}
}
return null;
}
private static string ReadFirstString(JsonValue obj, params string[] keys)
{
return GetFirst(obj, keys)?.AsString();
}
}
}