using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BuyMoreFixed.Configuration;
using BuyMoreFixed.Localization;
using HarmonyLib;
using Newtonsoft.Json;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BuyMoreFixed")]
[assembly: AssemblyDescription("BuyMoreFixed mod for Old Market Simulator by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("BuyMoreFixed")]
[assembly: AssemblyCopyright("Copyright © 2025 Ice Box Studio All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("EAAD3296-E8E3-4C52-BF86-6907CB4F80E6")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BuyMoreFixed
{
[BepInPlugin("IceBoxStudio.BuyMoreFixed", "BuyMoreFixed", "1.0.0")]
[BepInIncompatibility("FateArth.BuyMorePlugin")]
public class BuyMoreFixed : BaseUnityPlugin
{
private static BuyMoreFixed _instance;
private Harmony _harmony;
private bool patchesApplied;
internal static ManualLogSource Logger { get; private set; }
public static BuyMoreFixed Instance => _instance;
private void Awake()
{
_instance = this;
Logger = ((BaseUnityPlugin)this).Logger;
try
{
_ = LocalizationManager.Instance;
Logger.LogInfo((object)"=============================================");
Logger.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.initializing"));
Logger.LogInfo((object)(LocalizationManager.Instance.GetLocalizedText("plugin.author_prefix") + "Ice Box Studio(https://steamcommunity.com/id/ibox666/)"));
ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
ApplyPatches();
Logger.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.initialized"));
Logger.LogInfo((object)"=============================================");
}
catch (Exception ex)
{
Logger.LogError((object)("BuyMoreFixed 初始化错误: " + ex.Message + "\n" + ex.StackTrace));
}
}
private void ApplyPatches()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
if (!patchesApplied)
{
try
{
Logger.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.applying_patches"));
_harmony = new Harmony("IceBoxStudio.BuyMoreFixed");
_harmony.PatchAll();
patchesApplied = true;
Logger.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.patches_applied"));
return;
}
catch (Exception ex)
{
Logger.LogError((object)("BuyMoreFixed 应用补丁错误: " + ex.Message + "\n" + ex.StackTrace));
return;
}
}
Logger.LogInfo((object)LocalizationManager.Instance.GetLocalizedText("plugin.patches_skipped"));
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "IceBoxStudio.BuyMoreFixed";
public const string PLUGIN_NAME = "BuyMoreFixed";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace BuyMoreFixed.Patches
{
public static class GameManagerPatches
{
[HarmonyPatch(typeof(GameManager), "AddDockOrderServerRpc")]
public static class AddDockOrderServerRpcPatch
{
private static bool Prefix(GameManager __instance, long itemId)
{
if (!ConfigManager.EnableMod.Value)
{
return true;
}
NetworkManager networkManager = ((NetworkBehaviour)__instance).NetworkManager;
if ((Object)(object)networkManager == (Object)null || !networkManager.IsListening)
{
return false;
}
if (!networkManager.IsServer && !networkManager.IsHost)
{
return false;
}
return ExecuteOrderLogicWithoutLimit(__instance, itemId);
}
}
[HarmonyPatch(typeof(GameManager), "CheckDay")]
public static class CheckDayPatch
{
private static bool Prefix(GameManager __instance)
{
if (!ConfigManager.EnableMod.Value)
{
return true;
}
FieldInfo fieldInfo = AccessTools.Field(typeof(GameManager), "activeDockOrders");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(GameManager), "dockOrderSpawnPositions");
NetworkList<long> val = fieldInfo.GetValue(__instance) as NetworkList<long>;
object? value = fieldInfo2.GetValue(__instance);
Transform val2 = (Transform)((value is Transform) ? value : null);
if (val != null && (Object)(object)val2 != (Object)null && val.Count > 0)
{
int count = val.Count;
int childCount = val2.childCount;
if (count > childCount)
{
CallOriginalCheckDayWithoutOrderProcessing(__instance);
ProcessOrdersSafely(__instance, val, val2);
val.Clear();
AccessTools.Method(typeof(GameManager), "CalculateCapacity", new Type[0], (Type[])null)?.Invoke(__instance, null);
UIManager.Instance.CloseEndOfDayPanel();
return false;
}
return true;
}
return true;
}
private static void CallOriginalCheckDayWithoutOrderProcessing(GameManager instance)
{
MethodInfo methodInfo = AccessTools.Method(typeof(GameManager), "GetCurrentEvent", new Type[0], (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(typeof(GameManager), "GetCurrentSeason", new Type[0], (Type[])null);
MethodInfo methodInfo3 = AccessTools.Method(typeof(GameManager), "GetCurrentDay", new Type[0], (Type[])null);
object obj = methodInfo?.Invoke(instance, null);
object obj2 = methodInfo2?.Invoke(instance, null);
object obj3 = methodInfo3?.Invoke(instance, null);
if (obj2 != null && obj3 != null)
{
MethodInfo methodInfo4 = AccessTools.Method(typeof(UIManager), "SetDateText", new Type[2]
{
typeof(int),
typeof(string)
}, (Type[])null);
MethodInfo methodInfo5 = AccessTools.Method(typeof(UIManager), "SetEventText", new Type[1] { typeof(EventSO) }, (Type[])null);
MethodInfo methodInfo6 = AccessTools.Method(typeof(UIManager), "SetEventTooltip", new Type[1] { typeof(string) }, (Type[])null);
UIManager instance2 = UIManager.Instance;
methodInfo4?.Invoke(instance2, new object[2]
{
obj3,
AccessTools.Field(obj2.GetType(), "title")?.GetValue(obj2)
});
methodInfo5?.Invoke(instance2, new object[1] { obj });
methodInfo6?.Invoke(instance2, new object[1] { "" });
}
}
private static void ProcessOrdersSafely(GameManager instance, NetworkList<long> activeDockOrders, Transform dockOrderSpawnPositions)
{
//IL_01e2: 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_0207: Unknown result type (might be due to invalid IL or missing references)
int childCount = dockOrderSpawnPositions.childCount;
int count = activeDockOrders.Count;
for (int i = 0; i < count; i++)
{
long num = activeDockOrders[i];
object obj = AccessTools.Method(typeof(GameManager), "GetItemById", new Type[1] { typeof(long) }, (Type[])null)?.Invoke(instance, new object[1] { num });
int num2 = i % childCount;
Transform child = dockOrderSpawnPositions.GetChild(num2);
MethodInfo methodInfo = AccessTools.Method(typeof(GameManager), "GetWholesalePrice", new Type[2]
{
typeof(ProductSO),
typeof(int)
}, (Type[])null);
object obj2 = AccessTools.Field(typeof(GameManager), "day")?.GetValue(instance);
int num3 = (int)AccessTools.Property(obj2?.GetType(), "Value")?.GetValue(obj2) - 1;
object obj3 = methodInfo?.Invoke(instance, new object[2] { obj, num3 });
MethodInfo methodInfo2 = AccessTools.Method(typeof(GameManager), "SpawnItemAtPositionServerRpc", new Type[8]
{
typeof(long),
typeof(Vector3),
typeof(Quaternion),
typeof(bool),
typeof(Vector3),
typeof(int),
typeof(int),
typeof(int)
}, (Type[])null);
object obj4 = AccessTools.Field(obj?.GetType(), "amount")?.GetValue(obj);
methodInfo2?.Invoke(instance, new object[8]
{
num,
child.position,
child.rotation,
false,
Vector3.zero,
obj4,
obj3,
0
});
}
if (count > 0)
{
MethodInfo methodInfo3 = AccessTools.Method(typeof(GameManager), "ShowNotificationClientRpc", new Type[4]
{
typeof(string),
typeof(bool),
typeof(float),
typeof(bool)
}, (Type[])null);
string text = ((count > childCount) ? LocalizationManager.Instance.GetLocalizedText("order.arrival_notification_exceeded", count, childCount) : LocalizationManager.Instance.GetLocalizedText("order.arrival_notification"));
methodInfo3?.Invoke(instance, new object[4] { text, false, 5f, false });
}
}
}
private static bool ExecuteOrderLogicWithoutLimit(GameManager instance, long itemId)
{
object value = AccessTools.Field(typeof(GameManager), "activeDockOrders").GetValue(instance);
int num = (int)AccessTools.Property(value.GetType(), "Count").GetValue(value);
if (!ConfigManager.RemoveOrderLimit.Value)
{
object? value2 = AccessTools.Field(typeof(GameManager), "dockOrderSpawnPositions").GetValue(instance);
object? obj = ((value2 is Transform) ? value2 : null);
int effectiveOrderLimit = ConfigManager.GetEffectiveOrderLimit((obj != null) ? ((Transform)obj).childCount : 10);
if (num >= effectiveOrderLimit)
{
return false;
}
}
object obj2 = AccessTools.Method(typeof(GameManager), "GetItemById", new Type[1] { typeof(long) }, (Type[])null).Invoke(instance, new object[1] { itemId });
if (obj2 == null)
{
return false;
}
int num2 = (int)AccessTools.Method(typeof(GameManager), "GetWholesalePrice", new Type[1] { typeof(ProductSO) }, (Type[])null).Invoke(instance, new object[1] { obj2 });
int num3 = (int)AccessTools.Field(obj2.GetType(), "amount").GetValue(obj2);
int num4 = num2 * num3;
object value3 = AccessTools.Field(typeof(GameManager), "coins").GetValue(instance);
if ((int)AccessTools.Property(value3.GetType(), "Value").GetValue(value3) >= num4)
{
MethodInfo methodInfo = AccessTools.Method(typeof(GameManager), "IncrementCoinsServer", new Type[3]
{
typeof(int),
typeof(TransactionType),
typeof(string)
}, (Type[])null);
string text = (string)AccessTools.Field(obj2.GetType(), "itemName").GetValue(obj2);
methodInfo.Invoke(instance, new object[3]
{
-num4,
(object)(TransactionType)1,
text
});
MethodInfo methodInfo2 = AccessTools.Method(value.GetType(), "Add", (Type[])null, (Type[])null);
long num5 = (long)AccessTools.Field(obj2.GetType(), "id").GetValue(obj2);
methodInfo2.Invoke(value, new object[1] { num5 });
object value4 = AccessTools.Field(typeof(GameManager), "tutorialOrder").GetValue(instance);
if (value4 != null)
{
long num6 = (long)AccessTools.Field(value4.GetType(), "id").GetValue(value4);
AccessTools.Method(typeof(GameManager), "FinishTutorialServerRpc", new Type[1] { typeof(long) }, (Type[])null).Invoke(instance, new object[1] { num6 });
}
}
return false;
}
}
[HarmonyPatch(typeof(UIManager))]
public static class UIManagerPatches
{
[HarmonyPatch("UpdateDockOrdersPanel")]
[HarmonyPostfix]
public static void UpdateDockOrdersPanel_Postfix(UIManager __instance)
{
if (!ConfigManager.EnableMod.Value)
{
return;
}
FieldInfo fieldInfo = AccessTools.Field(typeof(UIManager), "textDockOrderSize");
if (fieldInfo == null)
{
return;
}
object? value = fieldInfo.GetValue(__instance);
TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
if ((Object)(object)val == (Object)null)
{
return;
}
int num = GameManager.Instance.GetActiveDockOrders()?.Count ?? 0;
string text;
if (ConfigManager.RemoveOrderLimit.Value)
{
text = $"{num}/∞";
}
else
{
FieldInfo fieldInfo2 = AccessTools.Field(typeof(GameManager), "dockOrderSpawnPositions");
if (fieldInfo2 != null)
{
object? value2 = fieldInfo2.GetValue(GameManager.Instance);
Transform val2 = (Transform)((value2 is Transform) ? value2 : null);
if ((Object)(object)val2 != (Object)null)
{
int effectiveOrderLimit = ConfigManager.GetEffectiveOrderLimit(val2.childCount);
text = $"{num}/{effectiveOrderLimit}";
}
else
{
text = $"{num}/∞";
}
}
else
{
text = $"{num}/∞";
}
}
((TMP_Text)val).text = text;
}
}
}
namespace BuyMoreFixed.Localization
{
public class LocalizationManager
{
private static LocalizationManager _instance;
private Dictionary<string, Dictionary<string, string>> _localizations = new Dictionary<string, Dictionary<string, string>>();
private string _currentLocale = "zh";
public static readonly string[] SupportedLanguages = new string[12]
{
"zh", "zh-Hant", "en", "fr", "de", "ja", "ko", "pt", "ru", "es",
"tr", "uk"
};
private static readonly string[] DefaultTranslationLanguages = new string[3] { "zh", "zh-Hant", "en" };
public static LocalizationManager Instance => _instance ?? (_instance = new LocalizationManager());
private LocalizationManager()
{
Initialize();
}
public void Initialize()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)LocalizationSettings.SelectedLocale != (Object)null)
{
LocaleIdentifier identifier = LocalizationSettings.SelectedLocale.Identifier;
_currentLocale = ((LocaleIdentifier)(ref identifier)).Code;
}
LocalizationHelper.InitializeLanguageFiles();
string path = Path.Combine(Paths.ConfigPath, "BuyMoreFixed", "Localization");
string[] supportedLanguages = SupportedLanguages;
foreach (string text in supportedLanguages)
{
string filePath = Path.Combine(path, text + ".json");
LoadLanguageFile(text, filePath);
}
LocalizationSettings.SelectedLocaleChanged += OnGameLanguageChanged;
}
private void OnGameLanguageChanged(Locale locale)
{
//IL_000a: 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)
if ((Object)(object)locale != (Object)null)
{
LocaleIdentifier identifier = locale.Identifier;
string code = ((LocaleIdentifier)(ref identifier)).Code;
if (_localizations.ContainsKey(code))
{
_currentLocale = code;
return;
}
_currentLocale = "en";
BuyMoreFixed.Logger.LogInfo((object)GetLocalizedText("plugin.language_fallback", code));
}
}
private void LoadLanguageFile(string language, string filePath)
{
if (File.Exists(filePath))
{
try
{
Dictionary<string, string> value = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(filePath));
_localizations[language] = value;
return;
}
catch
{
if (Array.IndexOf(DefaultTranslationLanguages, language) >= 0)
{
_localizations[language] = GetDefaultTranslations(language);
}
return;
}
}
if (Array.IndexOf(DefaultTranslationLanguages, language) >= 0)
{
_localizations[language] = GetDefaultTranslations(language);
}
}
private Dictionary<string, string> GetDefaultTranslations(string language)
{
MethodInfo methodInfo = AccessTools.Method(typeof(LocalizationHelper), "GetDefaultTranslations", (Type[])null, (Type[])null);
if (methodInfo != null)
{
return methodInfo.Invoke(null, new object[1] { language }) as Dictionary<string, string>;
}
return new Dictionary<string, string>();
}
public string GetLocalizedText(string key, params object[] args)
{
if (string.IsNullOrEmpty(_currentLocale) || !_localizations.ContainsKey(_currentLocale))
{
_currentLocale = "en";
}
if (_localizations.ContainsKey(_currentLocale) && _localizations[_currentLocale].TryGetValue(key, out var value))
{
return string.Format(value, args);
}
if (_currentLocale != "en" && _localizations.ContainsKey("en") && _localizations["en"].TryGetValue(key, out var value2))
{
return string.Format(value2, args);
}
if (_localizations.ContainsKey("zh") && _localizations["zh"].TryGetValue(key, out var value3))
{
return string.Format(value3, args);
}
return string.Format(key, args);
}
}
public static class LocalizationHelper
{
private static readonly string[] DefaultTranslationLanguages = new string[3] { "zh", "zh-Hant", "en" };
public static void InitializeLanguageFiles()
{
string text = Path.Combine(Paths.ConfigPath, "BuyMoreFixed", "Localization");
if (!Directory.Exists(text))
{
Directory.CreateDirectory(text);
}
string[] defaultTranslationLanguages = DefaultTranslationLanguages;
foreach (string text2 in defaultTranslationLanguages)
{
string filePath = Path.Combine(text, text2 + ".json");
EnsureLanguageFile(text2, filePath);
}
}
private static void EnsureLanguageFile(string language, string filePath)
{
Dictionary<string, string> defaultTranslations = GetDefaultTranslations(language);
if (File.Exists(filePath))
{
try
{
Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(filePath));
bool flag = false;
foreach (KeyValuePair<string, string> item in defaultTranslations)
{
if (!dictionary.ContainsKey(item.Key))
{
dictionary[item.Key] = item.Value;
flag = true;
}
}
if (flag)
{
string contents = JsonConvert.SerializeObject((object)dictionary, (Formatting)1);
File.WriteAllText(filePath, contents);
}
return;
}
catch
{
CreateLanguageFile(language, filePath, defaultTranslations);
return;
}
}
CreateLanguageFile(language, filePath, defaultTranslations);
}
private static void CreateLanguageFile(string language, string filePath, Dictionary<string, string> translations)
{
string contents = JsonConvert.SerializeObject((object)translations, (Formatting)1);
File.WriteAllText(filePath, contents);
}
public static Dictionary<string, string> GetDefaultTranslations(string language)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
switch (language)
{
case "zh":
dictionary.Add("plugin.initializing", "BuyMoreFixed 开始初始化...");
dictionary.Add("plugin.author_prefix", "作者:");
dictionary.Add("plugin.initialized", "BuyMoreFixed 初始化成功!");
dictionary.Add("plugin.applying_patches", "正在应用 BuyMoreFixed 补丁...");
dictionary.Add("plugin.patches_applied", "BuyMoreFixed 补丁已成功应用!");
dictionary.Add("plugin.patches_skipped", "补丁已应用,跳过...");
dictionary.Add("plugin.language_fallback", "不支持的语言 {0},使用英语作为备用。");
dictionary.Add("config.enable_mod_desc", "是否启用 BuyMoreFixed 模组功能");
dictionary.Add("config.remove_order_limit_desc", "是否完全移除订购数量限制");
dictionary.Add("config.custom_order_limit_desc", "自定义的订购数量上限(当移除订购限制为false时生效,设为0或负数使用游戏默认值)");
dictionary.Add("order.arrival_notification", "订单已到达!");
dictionary.Add("order.arrival_notification_exceeded", "订单已到达!({0} 个订单使用 {1} 个位置循环生成)");
break;
case "zh-Hant":
dictionary.Add("plugin.initializing", "BuyMoreFixed 開始初始化...");
dictionary.Add("plugin.author_prefix", "作者:");
dictionary.Add("plugin.initialized", "BuyMoreFixed 初始化成功!");
dictionary.Add("plugin.applying_patches", "正在應用 BuyMoreFixed 補丁...");
dictionary.Add("plugin.patches_applied", "BuyMoreFixed 補丁已成功應用!");
dictionary.Add("plugin.patches_skipped", "補丁已應用,跳過...");
dictionary.Add("plugin.language_fallback", "不支持的語言 {0},使用英語作為備用。");
dictionary.Add("config.enable_mod_desc", "是否啟用 BuyMoreFixed 模組功能");
dictionary.Add("config.remove_order_limit_desc", "是否完全移除訂購數量限制");
dictionary.Add("config.custom_order_limit_desc", "自定義的訂購數量上限(當移除訂購限制為false時生效,設為0或負數使用遊戲默認值)");
dictionary.Add("order.arrival_notification", "訂單已到達!");
dictionary.Add("order.arrival_notification_exceeded", "訂單已到達!({0} 個訂單使用 {1} 個位置循環生成)");
break;
case "en":
dictionary.Add("plugin.initializing", "BuyMoreFixed is initializing...");
dictionary.Add("plugin.author_prefix", "Author: ");
dictionary.Add("plugin.initialized", "BuyMoreFixed initialized successfully!");
dictionary.Add("plugin.applying_patches", "Applying BuyMoreFixed patches...");
dictionary.Add("plugin.patches_applied", "BuyMoreFixed patches applied successfully!");
dictionary.Add("plugin.patches_skipped", "Patches already applied, skipping...");
dictionary.Add("plugin.language_fallback", "Unsupported language {0}, using English as fallback.");
dictionary.Add("config.enable_mod_desc", "Whether to enable BuyMoreFixed mod functionality");
dictionary.Add("config.remove_order_limit_desc", "Whether to completely remove order quantity limits");
dictionary.Add("config.custom_order_limit_desc", "Custom order quantity limit (takes effect when Remove Order Limit is false, set to 0 or negative to use game default)");
dictionary.Add("order.arrival_notification", "Orders arrived!");
dictionary.Add("order.arrival_notification_exceeded", "Orders arrived! ({0} orders using {1} positions cyclically)");
break;
}
return dictionary;
}
}
}
namespace BuyMoreFixed.Configuration
{
public static class ConfigManager
{
public static ConfigEntry<bool> EnableMod { get; private set; }
public static ConfigEntry<bool> RemoveOrderLimit { get; private set; }
public static ConfigEntry<int> CustomOrderLimit { get; private set; }
public static void Initialize(ConfigFile config)
{
EnableMod = config.Bind<bool>("General", "EnableMod", true, LocalizationManager.Instance.GetLocalizedText("config.enable_mod_desc"));
RemoveOrderLimit = config.Bind<bool>("General", "RemoveOrderLimit", true, LocalizationManager.Instance.GetLocalizedText("config.remove_order_limit_desc"));
CustomOrderLimit = config.Bind<int>("General", "CustomOrderLimit", 100, LocalizationManager.Instance.GetLocalizedText("config.custom_order_limit_desc"));
}
public static int GetEffectiveOrderLimit(int originalLimit)
{
if (RemoveOrderLimit.Value)
{
return int.MaxValue;
}
if (CustomOrderLimit.Value <= 0)
{
return originalLimit;
}
return CustomOrderLimit.Value;
}
}
}