using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
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: 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.towerfactory";
public const string PLUGIN_NAME = "TowerFactory";
public const string PLUGIN_VERSION = "1.0.0";
}
namespace TowerFactory
{
internal class ItemAmounts
{
public readonly int[] ids;
public readonly int[] counts;
public ItemAmounts(IDictionary<int, int> amounts)
{
ids = amounts.Keys.ToArray();
counts = ids.Select((int id) => amounts[id]).ToArray();
}
public static ItemAmounts Of(int[] ids, int[] counts)
{
Dictionary<int, int> dictionary = new Dictionary<int, int>();
for (int i = 0; i < ids.Length; i++)
{
dictionary.TryGetValue(ids[i], out var value);
dictionary[ids[i]] = value + counts[i];
}
return new ItemAmounts(dictionary);
}
public void Register(int[] register, int times)
{
lock (register)
{
for (int i = 0; i < ids.Length; i++)
{
register[ids[i]] += counts[i] * times;
}
}
}
}
internal class PseudoRecipe
{
public string name;
public ERecipeType buildingType;
public int[] ingredients;
public int[] ingredientCounts;
public Dictionary<int, int> outputs;
public ItemAmounts produced;
public ItemAmounts consumed;
public int ticks;
}
internal static class PseudoRecipes
{
private const int Coal = 1006;
private const int CrudeOil = 1007;
private const int FireIce = 1011;
private const int OpticalGrating = 1014;
private const int Graphite = 1109;
private const int RefinedOil = 1114;
private const int TitaniumCrystal = 1118;
private const int Hydrogen = 1120;
private const int Graphene = 1123;
private const int Casimir = 1126;
private const int EnergyMatrix = 6002;
private static readonly Lazy<List<PseudoRecipe>> all = new Lazy<List<PseudoRecipe>>(Build);
public static List<PseudoRecipe> All => all.Value;
private static List<PseudoRecipe> Build()
{
List<PseudoRecipe> list = new List<PseudoRecipe>();
RecipeProto item = Find((ERecipeType)3, new int[1] { 1007 }, 1114);
RecipeProto item2 = Find((ERecipeType)3, new int[2] { 1114, 1120 }, 1109);
RecipeProto item3 = Find((ERecipeType)3, new int[3] { 1114, 1120, 1006 }, 1114);
RecipeProto item4 = Find((ERecipeType)15, new int[2] { 1109, 1120 }, 6002);
RecipeProto grapheneRecipe = Find((ERecipeType)2, new int[1] { 1011 }, 1123);
RecipeProto target = Find((ERecipeType)4, new int[3] { 1118, 1123, 1120 }, 1126);
RecipeProto target2 = Find((ERecipeType)4, new int[3] { 1014, 1123, 1120 }, 1126);
Add(list, "能量矩阵(原油+石墨)", (ERecipeType)3, new int[2] { 1007, 1109 }, (item, 2), (item2, 4), (item4, 3));
Add(list, "X 射线裂解(净)", (ERecipeType)3, new int[1] { 1114 }, (item2, 1));
Add(list, "精炼油(原油+煤)", (ERecipeType)3, new int[2] { 1007, 1006 }, (item, 1), (item3, 1));
AddGrapheneSwap(list, "卡西米尔晶体(可燃冰)", target, grapheneRecipe);
AddGrapheneSwap(list, "卡西米尔晶体(光栅石+可燃冰)", target2, grapheneRecipe);
return list;
}
private static void AddGrapheneSwap(List<PseudoRecipe> list, string name, RecipeProto target, RecipeProto grapheneRecipe)
{
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
if (target == null || grapheneRecipe == null)
{
TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:找不到对应的真实配方"));
return;
}
int num = target.ItemCounts[Array.IndexOf(target.Items, 1123)];
int num2 = grapheneRecipe.ResultCounts[Array.IndexOf(grapheneRecipe.Results, 1123)];
int num3 = num / Gcd(num, num2) * num2;
int[] ingredientOrder = target.Items.Select((int id) => (id != 1123) ? id : 1011).ToArray();
Add(list, name, target.Type, ingredientOrder, (grapheneRecipe, num3 / num2), (target, num3 / num));
}
private static void Add(List<PseudoRecipe> list, string name, ERecipeType buildingType, int[] ingredientOrder, params (RecipeProto recipe, int times)[] steps)
{
//IL_0203: Unknown result type (might be due to invalid IL or missing references)
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
if (steps.Any(((RecipeProto recipe, int times) step) => step.recipe == null))
{
TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:找不到对应的真实配方"));
return;
}
Dictionary<int, int> dictionary = new Dictionary<int, int>();
Dictionary<int, int> dictionary2 = new Dictionary<int, int>();
int num = 0;
for (int num2 = 0; num2 < steps.Length; num2++)
{
var (val, num3) = steps[num2];
Accumulate(dictionary, val.Results, val.ResultCounts, num3);
Accumulate(dictionary2, val.Items, val.ItemCounts, num3);
num += val.TimeSpend * num3;
}
Dictionary<int, int> net = new Dictionary<int, int>(dictionary);
foreach (KeyValuePair<int, int> item in dictionary2)
{
net.TryGetValue(item.Key, out var value);
net[item.Key] = value - item.Value;
}
List<int> list2 = (from pair in net
where pair.Value < 0
select pair.Key).ToList();
if (list2.Count != ingredientOrder.Length || !ingredientOrder.All(list2.Contains))
{
string text = string.Join("、", list2.Select((int id) => $"{id}×{-net[id]}"));
TowerFactory.Log.LogWarning((object)("伪配方 " + name + " 未启用:按真实配方算出的净原料为 " + text + ",与设计不符"));
return;
}
PseudoRecipe pseudo = new PseudoRecipe
{
name = name,
buildingType = buildingType,
ingredients = ingredientOrder,
ingredientCounts = ingredientOrder.Select((int id) => -net[id]).ToArray(),
outputs = net.Where((KeyValuePair<int, int> pair) => pair.Value > 0).ToDictionary((KeyValuePair<int, int> pair) => pair.Key, (KeyValuePair<int, int> pair) => pair.Value),
produced = new ItemAmounts(dictionary),
consumed = new ItemAmounts(dictionary2),
ticks = Math.Max(num, 1)
};
list.Add(pseudo);
string text2 = string.Join(" + ", pseudo.ingredients.Select((int id, int i) => $"{pseudo.ingredientCounts[i]} {ItemName(id)}"));
string text3 = string.Join(" + ", pseudo.outputs.Select((KeyValuePair<int, int> pair) => $"{pair.Value} {ItemName(pair.Key)}"));
TowerFactory.Log.LogInfo((object)$"伪配方 {name}:{text2} → {text3},{(double)num / 60.0:0.##} 秒");
}
private static void Accumulate(Dictionary<int, int> target, int[] ids, int[] counts, int times)
{
for (int i = 0; i < ids.Length; i++)
{
target.TryGetValue(ids[i], out var value);
target[ids[i]] = value + counts[i] * times;
}
}
private static RecipeProto Find(ERecipeType type, int[] items, int result)
{
//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)
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto recipe in dataArray)
{
if (recipe != null && recipe.Type == type && Array.IndexOf(recipe.Results, result) >= 0 && recipe.Items.Length == items.Length && items.All((int id) => Array.IndexOf(recipe.Items, id) >= 0))
{
return recipe;
}
}
return null;
}
private static int Gcd(int a, int b)
{
while (b != 0)
{
int num = b;
b = a % b;
a = num;
}
return a;
}
private static string ItemName(int itemId)
{
ItemProto obj = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
return ((obj != null) ? ((Proto)obj).name : null) ?? itemId.ToString();
}
}
[HarmonyPatch]
public class StationProductionPatch
{
private enum PlanKind
{
Recipe,
Miner,
Gamma
}
private class Plan
{
public PlanKind kind;
public string description;
public int[] items;
public int[] itemCounts;
public int resultId;
public int resultCount;
public double recipeTicks;
public ItemAmounts statProduced;
public ItemAmounts statConsumed;
public int[] outputSlots;
public int outputMultiplier;
public double minerTicksPerItem;
public double photonTicks;
public long photonHeat;
}
private class State
{
public int[] signature = Array.Empty<int>();
public Plan plan;
public int stableTicks;
public bool active;
public double progress;
public long energyBuffer;
}
private const int ActivateDelayTicks = 60;
private const int CycleDivisor = 10;
private const int FirstIngredientSlot = 2;
private const string SlotsNotEnoughMessage = "原料格子不够,请加装运输塔扩容mod";
private const double VeinMinerTicksPerItem = 120.0;
private const double OilExtractorTicksPerItem = 60.0;
private const int AdvancedMinerMultiplier = 3;
private const double FractionatorTicksPerItem = 300.0;
private const float GammaPhotonModeMultiplier = 8f;
private const float GammaFullWarmupMultiplier = 2.5f;
private const float GammaFullWarmupLossFactor = 0.6f;
private static readonly ConditionalWeakTable<StationComponent, State> states = new ConditionalWeakTable<StationComponent, State>();
internal static string Tr(string zh, string en)
{
if (!Localization.isZHCN)
{
return en;
}
return zh;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlanetTransport), "GameTick")]
public static void PlanetTransport_GameTick_Postfix(PlanetTransport __instance)
{
PlanetFactory factory = __instance.factory;
FactoryProductionStat val = GameMain.statistics.production.factoryStatPool[factory.index];
StationComponent[] stationPool = __instance.stationPool;
for (int i = 1; i < __instance.stationCursor; i++)
{
StationComponent val2 = stationPool[i];
if (val2 != null && val2.id == i && !val2.isCollector && !val2.isVeinCollector)
{
Tick(factory, val2, val.productRegister, val.consumeRegister);
}
}
}
private static void Tick(PlanetFactory factory, StationComponent station, int[] productRegister, int[] consumeRegister)
{
StationStore[] storage = station.storage;
if (storage == null)
{
return;
}
State orCreateValue = states.GetOrCreateValue(station);
if (!SignatureEquals(orCreateValue.signature, storage))
{
Plan plan = orCreateValue.plan;
orCreateValue.signature = BuildSignature(storage);
orCreateValue.plan = MatchPlan(factory, station, out var reason);
orCreateValue.stableTicks = 0;
orCreateValue.active = false;
orCreateValue.progress = 0.0;
orCreateValue.energyBuffer = 0L;
LogMatch(factory, station, plan, orCreateValue.plan, reason);
}
Plan plan2 = orCreateValue.plan;
if (plan2 == null)
{
return;
}
if (!orCreateValue.active)
{
if (++orCreateValue.stableTicks < 60)
{
return;
}
orCreateValue.active = true;
TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 塔厂开始生产:" + plan2.description));
}
int count = storage[0].count;
if (count <= 0)
{
return;
}
int batch = (count + 10 - 1) / 10;
double period = GetPeriod(plan2);
if (period <= 0.0)
{
return;
}
if (plan2.kind == PlanKind.Gamma)
{
DrawGammaEnergy(factory, orCreateValue, plan2, batch);
}
orCreateValue.progress += 10.0;
while (orCreateValue.progress >= period)
{
orCreateValue.progress -= period;
switch (plan2.kind)
{
case PlanKind.Recipe:
CraftRecipe(station, plan2, batch, productRegister, consumeRegister);
break;
case PlanKind.Miner:
CraftMiner(station, plan2, batch, productRegister);
break;
case PlanKind.Gamma:
CraftGamma(station, orCreateValue, plan2, batch, productRegister);
break;
}
}
}
private static double GetPeriod(Plan plan)
{
switch (plan.kind)
{
case PlanKind.Recipe:
return plan.recipeTicks;
case PlanKind.Miner:
{
float miningSpeedScale = GameMain.history.miningSpeedScale;
if (!(miningSpeedScale > 0f))
{
return 0.0;
}
return plan.minerTicksPerItem / (double)miningSpeedScale;
}
case PlanKind.Gamma:
return plan.photonTicks;
default:
return 0.0;
}
}
private static void CraftRecipe(StationComponent station, Plan plan, int batch, int[] productRegister, int[] consumeRegister)
{
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
StationStore[] storage = station.storage;
int[] items = plan.items;
int[] itemCounts = plan.itemCounts;
int resultCount = plan.resultCount;
lock (storage)
{
int val = batch;
for (int i = 0; i < items.Length; i++)
{
val = Math.Min(val, storage[2 + i].count / itemCounts[i]);
}
val = Math.Min(val, FreeSpace(storage[1]) / resultCount);
if (val <= 0)
{
return;
}
for (int j = 0; j < items.Length; j++)
{
ref StationStore reference = ref storage[2 + j];
reference.count -= val * itemCounts[j];
if (reference.inc > reference.count)
{
reference.inc = reference.count;
}
}
storage[1].count += val * resultCount;
plan.statConsumed.Register(consumeRegister, val);
plan.statProduced.Register(productRegister, val);
}
}
private static void CraftMiner(StationComponent station, Plan plan, int batch, int[] productRegister)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
StationStore[] storage = station.storage;
lock (storage)
{
int[] outputSlots = plan.outputSlots;
foreach (int num in outputSlots)
{
int num2 = Math.Min(batch * plan.outputMultiplier, FreeSpace(storage[num]));
if (num2 > 0)
{
storage[num].count += num2;
lock (productRegister)
{
productRegister[storage[num].itemId] += num2;
}
}
}
}
}
private static long EnergyPerPhoton(Plan plan)
{
float num = 1f - GameMain.history.solarEnergyLossRate * 0.6f;
if (!(num > 0f))
{
return 0L;
}
return (long)((float)plan.photonHeat / num);
}
private static void DrawGammaEnergy(PlanetFactory factory, State state, Plan plan, int batch)
{
DysonSphere dysonSphere = factory.dysonSphere;
long num = EnergyPerPhoton(plan);
if (dysonSphere == null || num <= 0)
{
return;
}
long num2 = batch * num;
long num3 = Math.Min((long)((double)(num2 * 10) / plan.photonTicks) + 1, num2 - state.energyBuffer);
if (num3 <= 0)
{
return;
}
long num4;
long num5;
do
{
num4 = Interlocked.Read(in dysonSphere.energyReqCurrentTick);
long val = dysonSphere.energyGenCurrentTick - num4;
num5 = Math.Min(num3, val);
if (num5 <= 0)
{
return;
}
}
while (Interlocked.CompareExchange(ref dysonSphere.energyReqCurrentTick, num4 + num5, num4) != num4);
state.energyBuffer += num5;
}
private static void CraftGamma(StationComponent station, State state, Plan plan, int batch, int[] productRegister)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
long num = EnergyPerPhoton(plan);
if (num <= 0)
{
return;
}
StationStore[] storage = station.storage;
lock (storage)
{
int num2 = (int)Math.Min(Math.Min(batch, FreeSpace(storage[1])), state.energyBuffer / num);
if (num2 <= 0)
{
return;
}
state.energyBuffer -= num2 * num;
storage[1].count += num2;
lock (productRegister)
{
productRegister[storage[1].itemId] += num2;
}
}
}
private static int FreeSpace(StationStore store)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
int num = store.max - store.count;
if (num <= 0)
{
return 0;
}
return num;
}
private static Plan MatchPlan(PlanetFactory factory, StationComponent station, out string reason)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: 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_002b: 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)
//IL_004f: Invalid comparison between Unknown and I4
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Invalid comparison between Unknown and I4
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Invalid comparison between Unknown and I4
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
reason = null;
StationStore[] storage = station.storage;
if (storage.Length < 2)
{
return null;
}
StationStore val = storage[0];
PrefabDesc val2 = ((val.itemId <= 0) ? null : ((ProtoSet<ItemProto>)(object)LDB.items).Select(val.itemId)?.prefabDesc);
if (val2 == null)
{
return null;
}
bool flag = (int)val2.minerType == 2 || val2.isVeinCollector;
bool flag2 = (int)val2.minerType == 1;
bool flag3 = (int)val2.minerType == 3;
if (!val2.isAssembler && !val2.isLab && !val2.isFractionator && !flag && !flag2 && !flag3 && !val2.gammaRayReceiver && !val2.isPowerExchanger)
{
return null;
}
if ((int)val.localLogic != 0 || (station.isStellar && (int)val.remoteLogic != 0))
{
reason = "第一格设置了需求或供应";
return null;
}
if (storage[1].itemId <= 0)
{
reason = "第二格为空";
return null;
}
if (val2.isAssembler)
{
return MatchRecipePlan(storage, val2.assemblerRecipeType, out reason);
}
if (val2.isLab)
{
return MatchRecipePlan(storage, (ERecipeType)15, out reason);
}
if (val2.isFractionator)
{
return MatchFractionatorPlan(storage, out reason);
}
if (flag)
{
return MatchMinerPlan(factory, storage, val2.isVeinCollector, out reason);
}
if (flag2)
{
return MatchPumpPlan(factory, storage, val2, out reason);
}
if (flag3)
{
return MatchOilExtractorPlan(factory, storage, out reason);
}
if (val2.isPowerExchanger)
{
return MatchExchangerPlan(storage, val2, out reason);
}
return MatchGammaPlan(factory, storage, val2, out reason);
}
private static Plan MatchRecipePlan(StationStore[] storage, ERecipeType recipeType, out string reason)
{
//IL_002f: 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_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
reason = null;
int itemId = storage[1].itemId;
bool flag = false;
bool flag2 = false;
foreach (PseudoRecipe item in PseudoRecipes.All)
{
if (item.buildingType == recipeType && item.outputs.TryGetValue(itemId, out var value))
{
flag = true;
flag2 |= FitsSlotCount(storage, item.ingredients);
if (SlotsMatch(storage, item.ingredients))
{
return new Plan
{
kind = PlanKind.Recipe,
description = "伪配方 " + item.name,
items = item.ingredients,
itemCounts = item.ingredientCounts,
resultId = itemId,
resultCount = value,
recipeTicks = item.ticks,
statProduced = item.produced,
statConsumed = item.consumed
};
}
}
}
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val == null || val.Type != recipeType)
{
continue;
}
int num = Array.IndexOf(val.Results, itemId);
if (num >= 0)
{
flag = true;
flag2 |= FitsSlotCount(storage, val.Items);
if (SlotsMatch(storage, val.Items))
{
return new Plan
{
kind = PlanKind.Recipe,
description = "配方 " + ((Proto)val).name,
items = val.Items,
itemCounts = val.ItemCounts,
resultId = itemId,
resultCount = val.ResultCounts[num],
recipeTicks = Math.Max(val.TimeSpend, 1),
statProduced = ItemAmounts.Of(val.Results, val.ResultCounts),
statConsumed = ItemAmounts.Of(val.Items, val.ItemCounts)
};
}
}
}
if (!flag)
{
reason = "第二格 " + ItemName(itemId) + " 不是该建筑能生产的产品";
}
else if (!flag2)
{
reason = "原料格子不够,请加装运输塔扩容mod";
}
else
{
reason = "第三格起的原料与配方顺序不符";
}
return null;
}
private static bool FitsSlotCount(StationStore[] storage, int[] ingredients)
{
return 2 + ingredients.Length <= storage.Length;
}
private static Plan MatchFractionatorPlan(StationStore[] storage, out string reason)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Invalid comparison between Unknown and I4
reason = null;
if (storage.Length <= 2)
{
reason = "格子不够";
return null;
}
int itemId = storage[1].itemId;
int itemId2 = storage[2].itemId;
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val in dataArray)
{
if (val != null && (int)val.Type == 8 && val.Results.Length != 0 && val.Results[0] == itemId && val.Items.Length != 0 && val.Items[0] == itemId2)
{
Plan plan = new Plan();
plan.kind = PlanKind.Recipe;
plan.description = "分馏 " + ItemName(itemId2) + " → " + ItemName(itemId);
plan.items = new int[1] { itemId2 };
plan.itemCounts = new int[1] { 1 };
plan.resultId = itemId;
plan.resultCount = 1;
plan.recipeTicks = 300.0;
plan.statProduced = ItemAmounts.Of(new int[1] { itemId }, new int[1] { 1 });
plan.statConsumed = ItemAmounts.Of(new int[1] { itemId2 }, new int[1] { 1 });
return plan;
}
}
reason = "第二格/第三格不是分馏配方的产品/原料";
return null;
}
public static bool TryAutoFill(PlanetTransport transport, StationComponent station, out string message)
{
//IL_0019: 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)
int[] array = SuggestIngredients(station, out message);
if (array == null)
{
return false;
}
ELogisticStorage val = (ELogisticStorage)(station.isStellar ? 2 : 0);
for (int i = 0; i < array.Length; i++)
{
transport.SetStationStorage(station.id, 2 + i, array[i], int.MaxValue, (ELogisticStorage)2, val, (Player)null);
}
string text = string.Join(Tr("、", ", "), array.Select(ItemName));
message = Tr("已填入原料:" + text, "Ingredients filled: " + text);
TowerFactory.Log.LogInfo((object)(StationLabel(transport.factory, station) + " 已填入原料:" + text));
return true;
}
private static int[] SuggestIngredients(StationComponent station, out string reason)
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_0237: Unknown result type (might be due to invalid IL or missing references)
//IL_023c: Unknown result type (might be due to invalid IL or missing references)
//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
//IL_01c7: Invalid comparison between Unknown and I4
//IL_0360: Unknown result type (might be due to invalid IL or missing references)
StationStore[] storage = station?.storage;
if (storage == null || station.isCollector || station.isVeinCollector || storage.Length <= 2)
{
reason = Tr("这座塔不能做塔厂", "This station can't be a tower factory");
return null;
}
StationStore val = storage[0];
PrefabDesc val2 = ((val.itemId <= 0) ? null : ((ProtoSet<ItemProto>)(object)LDB.items).Select(val.itemId)?.prefabDesc);
if (val2 == null || (!val2.isAssembler && !val2.isLab && !val2.isFractionator && !val2.isPowerExchanger))
{
reason = Tr("第一格需要放有配方的生产建筑", "Slot 1 must hold a production building that uses recipes");
return null;
}
if ((int)val.localLogic != 0 || (station.isStellar && (int)val.remoteLogic != 0))
{
reason = Tr("第一格需要设为仓储", "Slot 1 must be set to Storage");
return null;
}
int productId = storage[1].itemId;
if (productId <= 0)
{
reason = Tr("第二格需要放产品", "Slot 2 must hold the product");
return null;
}
for (int i = 2; i < storage.Length; i++)
{
if (storage[i].itemId != 0)
{
reason = Tr("第三格起需要全部为空", "Slot 3 and later must all be empty");
return null;
}
}
List<int[]> list = new List<int[]>();
if (val2.isPowerExchanger)
{
if (productId == val2.fullId && val2.emptyId > 0)
{
list.Add(new int[1] { val2.emptyId });
}
}
else if (val2.isFractionator)
{
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
foreach (RecipeProto val3 in dataArray)
{
if (val3 != null && (int)val3.Type == 8 && val3.Results.Length != 0 && val3.Results[0] == productId && val3.Items.Length != 0)
{
list.Add(new int[1] { val3.Items[0] });
}
}
}
else
{
ERecipeType recipeType = (ERecipeType)((!val2.isAssembler) ? 15 : ((int)val2.assemblerRecipeType));
list.AddRange(from pseudo in PseudoRecipes.All
where pseudo.buildingType == recipeType && pseudo.outputs.ContainsKey(productId)
select pseudo.ingredients);
list.AddRange(from recipe in ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray
where recipe != null && recipe.Type == recipeType && Array.IndexOf(recipe.Results, productId) >= 0
select recipe.Items);
}
if (list.Count == 0)
{
reason = Tr(ItemName(productId) + " 不是该建筑能生产的产品", ItemName(productId) + " can't be produced by this building");
return null;
}
if (!list.Any((int[] ingredients) => FitsSlotCount(storage, ingredients)))
{
reason = Tr("原料格子不够,请加装运输塔扩容mod", "Not enough slots for the ingredients. Please install a station slot expansion mod");
return null;
}
foreach (int[] item in list)
{
if (FitsSlotCount(storage, item) && item.Distinct().Count() == item.Length && !item.Contains(val.itemId) && !item.Contains(productId))
{
reason = null;
return item;
}
}
reason = Tr("放得下的配方里,原料与塔里已有物品重复", "Every recipe that fits has an ingredient that duplicates an item already in the station");
return null;
}
private static Plan MatchExchangerPlan(StationStore[] storage, PrefabDesc prefabDesc, out string reason)
{
reason = null;
if (storage.Length <= 2)
{
reason = "原料格子不够,请加装运输塔扩容mod";
return null;
}
if (storage[1].itemId != prefabDesc.fullId || storage[2].itemId != prefabDesc.emptyId)
{
reason = "第二格应为 " + ItemName(prefabDesc.fullId) + ",第三格应为 " + ItemName(prefabDesc.emptyId);
return null;
}
if (prefabDesc.exchangeEnergyPerTick <= 0 || prefabDesc.maxExcEnergy <= 0)
{
reason = "能量枢纽功率数据异常";
return null;
}
double num = (double)prefabDesc.maxExcEnergy / (double)prefabDesc.exchangeEnergyPerTick;
Plan plan = new Plan();
plan.kind = PlanKind.Recipe;
plan.description = $"充电 {ItemName(prefabDesc.emptyId)} → {ItemName(prefabDesc.fullId)},单座每 {num / 60.0:0.##} 秒 1 个";
plan.items = new int[1] { prefabDesc.emptyId };
plan.itemCounts = new int[1] { 1 };
plan.resultId = prefabDesc.fullId;
plan.resultCount = 1;
plan.recipeTicks = num;
plan.statProduced = ItemAmounts.Of(new int[1] { prefabDesc.fullId }, new int[1] { 1 });
plan.statConsumed = ItemAmounts.Of(new int[1] { prefabDesc.emptyId }, new int[1] { 1 });
return plan;
}
private static bool SlotsMatch(StationStore[] storage, int[] ingredients)
{
if (2 + ingredients.Length > storage.Length)
{
return false;
}
for (int i = 0; i < ingredients.Length; i++)
{
if (storage[2 + i].itemId != ingredients[i])
{
return false;
}
}
return true;
}
private static Plan MatchMinerPlan(PlanetFactory factory, StationStore[] storage, bool isAdvanced, out string reason)
{
reason = null;
int num = ((!isAdvanced) ? 1 : (storage.Length - 1));
List<int> list = new List<int>();
List<string> list2 = new List<string>();
List<string> list3 = new List<string>();
for (int i = 1; i <= num; i++)
{
int itemId = storage[i].itemId;
if (itemId > 0)
{
string text = null;
if (!IsMineral(itemId))
{
text = $"第 {i + 1} 格 {ItemName(itemId)} 不是采矿机能采的矿物";
}
else if (!PlanetHasMineral(factory.planet, itemId))
{
text = $"本行星没有第 {i + 1} 格 {ItemName(itemId)} 的矿簇";
}
if (text != null)
{
list3.Add(text);
continue;
}
list.Add(i);
list2.Add(ItemName(itemId));
}
}
if (list.Count == 0)
{
reason = ((list3.Count > 0) ? string.Join(";", list3) : "第二格为空");
return null;
}
string text2 = ((list3.Count > 0) ? (";跳过:" + string.Join(";", list3)) : "");
return new Plan
{
kind = PlanKind.Miner,
description = (isAdvanced ? "大型采矿" : "采矿") + " " + string.Join("、", list2) + text2,
outputSlots = list.ToArray(),
outputMultiplier = ((!isAdvanced) ? 1 : 3),
minerTicksPerItem = 120.0
};
}
private static Plan MatchPumpPlan(PlanetFactory factory, StationStore[] storage, PrefabDesc prefabDesc, out string reason)
{
reason = null;
int num = factory.planet?.waterItemId ?? 0;
if (num <= 0)
{
reason = "本行星没有可抽取的海洋";
return null;
}
if (storage[1].itemId != num)
{
reason = "第二格应为本行星的海洋物品 " + ItemName(num);
return null;
}
if (prefabDesc.minerPeriod <= 0)
{
reason = "抽水机速度数据异常";
return null;
}
Plan plan = new Plan();
plan.kind = PlanKind.Miner;
plan.description = "抽取 " + ItemName(num);
plan.outputSlots = new int[1] { 1 };
plan.outputMultiplier = 1;
plan.minerTicksPerItem = (double)prefabDesc.minerPeriod / 10000.0;
return plan;
}
private static Plan MatchOilExtractorPlan(PlanetFactory factory, StationStore[] storage, out string reason)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Invalid comparison between Unknown and I4
reason = null;
int itemId = storage[1].itemId;
if ((int)LDB.veins.GetVeinTypeByItemId(itemId) != 7)
{
reason = "第二格 " + ItemName(itemId) + " 不是原油";
return null;
}
if (!PlanetHasMineral(factory.planet, itemId))
{
reason = "本行星没有油井";
return null;
}
Plan plan = new Plan();
plan.kind = PlanKind.Miner;
plan.description = "萃取 " + ItemName(itemId);
plan.outputSlots = new int[1] { 1 };
plan.outputMultiplier = 1;
plan.minerTicksPerItem = 60.0;
return plan;
}
private static bool IsMineral(int itemId)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: 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_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Invalid comparison between Unknown and I4
EVeinType veinTypeByItemId = LDB.veins.GetVeinTypeByItemId(itemId);
if ((int)veinTypeByItemId != 0)
{
return (int)veinTypeByItemId != 7;
}
return false;
}
private static bool PlanetHasMineral(PlanetData planet, int itemId)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: 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)
EVeinType veinTypeByItemId = LDB.veins.GetVeinTypeByItemId(itemId);
VeinGroup[] array = ((planet != null) ? planet.runtimeVeinGroups : null);
if (array == null)
{
return false;
}
VeinGroup[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
VeinGroup val = array2[i];
if (val.type == veinTypeByItemId && !((VeinGroup)(ref val)).isEmpty)
{
return true;
}
}
return false;
}
private static Plan MatchGammaPlan(PlanetFactory factory, StationStore[] storage, PrefabDesc prefabDesc, out string reason)
{
reason = null;
if (storage[1].itemId != prefabDesc.powerProductId || prefabDesc.powerProductHeat <= 0)
{
reason = "第二格应为 " + ItemName(prefabDesc.powerProductId);
return null;
}
float num = (float)prefabDesc.genEnergyPerTick * 8f * 2.5f;
if (num <= 0f)
{
reason = "射线接收站功率数据异常";
return null;
}
if (factory.dysonSphere == null)
{
reason = "本星系没有戴森球";
return null;
}
Plan plan = new Plan
{
kind = PlanKind.Gamma,
photonTicks = (double)prefabDesc.powerProductHeat / (double)num,
photonHeat = prefabDesc.powerProductHeat
};
plan.description = $"临界光子,单座每 {plan.photonTicks / 60.0:0.##} 秒 1 个,每个耗能 {(double)prefabDesc.powerProductHeat / 1000000.0:0.##} MJ";
return plan;
}
private static void LogMatch(PlanetFactory factory, StationComponent station, Plan previous, Plan plan, string reason)
{
if (plan != null)
{
TowerFactory.Log.LogInfo((object)$"{StationLabel(factory, station)} 识别为塔厂({plan.description}),{1} 秒后生效");
}
else if (reason != null)
{
TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 未成为塔厂:" + reason));
}
else if (previous != null)
{
TowerFactory.Log.LogInfo((object)(StationLabel(factory, station) + " 不再是塔厂"));
}
}
private static string StationLabel(PlanetFactory factory, StationComponent station)
{
PlanetData planet = factory.planet;
return $"[{((planet != null) ? planet.displayName : null)} 站点 #{station.id}]";
}
private static string ItemName(int itemId)
{
ItemProto obj = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId);
return ((obj != null) ? ((Proto)obj).name : null) ?? itemId.ToString();
}
private static int[] BuildSignature(StationStore[] storage)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected I4, but got Unknown
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Expected I4, but got Unknown
int[] array = new int[storage.Length * 3];
for (int i = 0; i < storage.Length; i++)
{
array[i * 3] = storage[i].itemId;
array[i * 3 + 1] = (int)storage[i].localLogic;
array[i * 3 + 2] = (int)storage[i].remoteLogic;
}
return array;
}
private static bool SignatureEquals(int[] signature, StationStore[] storage)
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Invalid comparison between I4 and Unknown
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Invalid comparison between I4 and Unknown
if (signature.Length != storage.Length * 3)
{
return false;
}
for (int i = 0; i < storage.Length; i++)
{
if (signature[i * 3] != storage[i].itemId || signature[i * 3 + 1] != (int)storage[i].localLogic || signature[i * 3 + 2] != (int)storage[i].remoteLogic)
{
return false;
}
}
return true;
}
}
[BepInPlugin("com.yummi.dsp.towerfactory", "塔厂 (Tower Factory)", "1.1.0")]
public class TowerFactory : BaseUnityPlugin
{
public const string PLUGIN_GUID = "com.yummi.dsp.towerfactory";
public const string PLUGIN_NAME = "塔厂 (Tower Factory)";
public const string PLUGIN_VERSION = "1.1.0";
private static readonly Harmony harmony = new Harmony("com.yummi.dsp.towerfactory");
internal static ManualLogSource Log;
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
harmony.PatchAll(typeof(StationProductionPatch));
harmony.PatchAll(typeof(UIStationWindowPatch));
Log.LogInfo((object)"塔厂 (Tower Factory) v1.1.0 已加载");
}
private void OnDestroy()
{
harmony.UnpatchSelf();
}
}
[HarmonyPatch(typeof(UIStationWindow))]
public static class UIStationWindowPatch
{
private static Button _autoFillButton;
private static Text _autoFillText;
[HarmonyPostfix]
[HarmonyPatch("OnStationIdChange")]
public static void OnStationIdChange_Postfix(UIStationWindow __instance)
{
EnsureButton(__instance);
if (!((Object)(object)_autoFillButton == (Object)null))
{
StationComponent station = GetStation(__instance);
bool flag = ((ManualBehaviour)__instance).active && station != null && !station.isCollector && !station.isVeinCollector;
((Component)_autoFillButton).gameObject.SetActive(flag);
if (flag)
{
UpdateButtonText();
}
}
}
private static void UpdateButtonText()
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
string text = StationProductionPatch.Tr("塔厂:填原料", "Fill Ingredients");
if (!(_autoFillText.text == text))
{
_autoFillText.text = text;
RectTransform val = (RectTransform)((Component)_autoFillButton).transform;
val.sizeDelta = new Vector2(Mathf.Max(100f, _autoFillText.preferredWidth + 16f), val.sizeDelta.y);
}
}
private static StationComponent GetStation(UIStationWindow window)
{
PlanetTransport transport = window.transport;
int stationId = window.stationId;
if (transport == null || stationId <= 0 || stationId >= transport.stationCursor)
{
return null;
}
StationComponent val = transport.stationPool[stationId];
if (val == null || val.id != stationId)
{
return null;
}
return val;
}
private static void EnsureButton(UIStationWindow window)
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: 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_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Expected O, but got Unknown
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
//IL_01da: Unknown result type (might be due to invalid IL or missing references)
//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_autoFillButton != (Object)null) && !((Object)(object)window.windowTrans == (Object)null))
{
GameObject val = new GameObject("tower-factory-autofill");
val.transform.SetParent((Transform)(object)window.windowTrans, false);
RectTransform obj = val.AddComponent<RectTransform>();
obj.anchorMin = new Vector2(1f, 1f);
obj.anchorMax = new Vector2(1f, 1f);
obj.pivot = new Vector2(1f, 1f);
obj.anchoredPosition = new Vector2(-20f, -36f);
obj.sizeDelta = new Vector2(100f, 22f);
((Graphic)val.AddComponent<Image>()).color = new Color(0.24f, 0.55f, 0.65f, 0.9f);
_autoFillButton = val.AddComponent<Button>();
ColorBlock colors = ((Selectable)_autoFillButton).colors;
((ColorBlock)(ref colors)).highlightedColor = new Color(1.2f, 1.2f, 1.2f, 1f);
((ColorBlock)(ref colors)).pressedColor = new Color(0.7f, 0.7f, 0.7f, 1f);
((Selectable)_autoFillButton).colors = colors;
((UnityEvent)_autoFillButton.onClick).AddListener((UnityAction)delegate
{
OnAutoFillClick(window);
});
GameObject val2 = new GameObject("Text");
val2.transform.SetParent(val.transform, false);
Text obj2 = (_autoFillText = val2.AddComponent<Text>());
obj2.font = (((Object)(object)window.titleText != (Object)null) ? window.titleText.font : Resources.GetBuiltinResource<Font>("Arial.ttf"));
obj2.fontSize = 14;
obj2.alignment = (TextAnchor)4;
((Graphic)obj2).color = Color.white;
RectTransform component = val2.GetComponent<RectTransform>();
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.offsetMin = Vector2.zero;
component.offsetMax = Vector2.zero;
}
}
private static void OnAutoFillClick(UIStationWindow window)
{
StationComponent station = GetStation(window);
if (station != null)
{
string message;
bool flag = StationProductionPatch.TryAutoFill(window.transport, station, out message);
UIRealtimeTip.Popup(message, !flag, 0);
if (flag)
{
window.stationId = window.stationId;
}
}
}
}
}