using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.Json; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.NET.Common; using Candide; using Candide.GameModels.Helpers; using CandideServer; using CandideServer.Game; using CandideServer.Helpers; using CandideServer.Models; using CandideServer.Models.Buildings; using CandideServer.Models.Citizen; using CandideServer.Server; using CandideServer.ServerControllers; using CandideServer.ServerManagers; using CandideServer.ServerServices; using CandideServer.ServerSystems; using CandideServer.SimulationModels; using CandideServer.SyncStrategies; using HarmonyLib; using Microsoft.Xna.Framework; using ModSettingsMenu.Api; using ProsperityTweaks.Configuration; using ProsperityTweaks.Localizations; using Shared.Models; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Prosperity Tweaks")] [assembly: AssemblyDescription("Prosperity Tweaks for Romestead by Ice Box Studio")] [assembly: AssemblyCompany("Ice Box Studio")] [assembly: AssemblyProduct("Prosperity Tweaks")] [assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")] [assembly: ComVisible(false)] [assembly: Guid("8e19e628-4eff-442f-9b7f-f4b975d0b306")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace ProsperityTweaks { public static class PluginInfo { public const string PLUGIN_GUID = "IceBoxStudio.Romestead.ProsperityTweaks"; public const string PLUGIN_NAME = "Prosperity Tweaks"; public const string PLUGIN_VERSION = "1.0.0"; public const string PLUGIN_AUTHOR = "Ice Box Studio"; public const string PLUGIN_AUTHOR_URL = "https://steamcommunity.com/id/ibox666/"; } [BepInPlugin("IceBoxStudio.Romestead.ProsperityTweaks", "Prosperity Tweaks", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ProsperityTweaks : BasePlugin { public static ProsperityTweaks _Instance; private Harmony _harmony; public static ProsperityTweaks Instance => _Instance; internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown _Instance = this; Logger = ((BasePlugin)this).Log; bool flag = default(bool); try { Logger.LogInfo((object)"============================================="); ManualLogSource logger = Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(16, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("Prosperity Tweaks"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initializing..."); } logger.LogInfo(val); ManualLogSource logger2 = Logger; val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Author: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("Ice Box Studio"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("https://steamcommunity.com/id/ibox666/"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } logger2.LogInfo(val); ConfigManager.Init(((BasePlugin)this).Config); ModSettingsMenuRegistration.Register(((BasePlugin)this).Config); _harmony = new Harmony("IceBoxStudio.Romestead.ProsperityTweaks"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ManualLogSource logger3 = Logger; val = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("Prosperity Tweaks"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initialized."); } logger3.LogInfo(val); Logger.LogInfo((object)"============================================="); } catch (Exception ex) { ManualLogSource logger4 = Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(24, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted("Prosperity Tweaks"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" initialization error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.StackTrace); } logger4.LogError(val2); } } } } namespace ProsperityTweaks.Patches { internal static class AlwaysWetFarmland { private static readonly List ChangedTiles = new List(); private static bool _wasEnabled; private static bool _needsCatchUp = true; private static int _lastCropCount = -1; public static bool Enabled => PatchHelper.ShouldApply && ConfigManager.KeepFarmlandWet; public static void Tick() { if (!Enabled) { _wasEnabled = false; _needsCatchUp = true; _lastCropCount = -1; return; } if (!CanUseWorldState()) { _needsCatchUp = true; return; } int count = ServerGameState.Crops.Count; if (!_wasEnabled || _needsCatchUp || count != _lastCropCount) { WetAllCropTiles(); _lastCropCount = ServerGameState.Crops.Count; _needsCatchUp = false; } _wasEnabled = true; } public static void MarkDirty() { _needsCatchUp = true; } public static void WetCropTile(CropServerModel crop, bool notify) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || crop == null) { return; } if (!CanUseWorldState()) { MarkDirty(); return; } ChangedTiles.Clear(); WetCropTile(crop, new FarmlandStatusModel(ServerGameState.Time), ChangedTiles); if (notify) { SendChangedTiles(); } if (!_needsCatchUp) { _lastCropCount = ServerGameState.Crops.Count; } } private static void WetAllCropTiles() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) ChangedTiles.Clear(); FarmlandStatusModel status = default(FarmlandStatusModel); ((FarmlandStatusModel)(ref status))..ctor(ServerGameState.Time); foreach (CropServerModel value in ServerGameState.Crops.Values) { WetCropTile(value, status, ChangedTiles); } SendChangedTiles(); } private static void WetCropTile(CropServerModel crop, FarmlandStatusModel status, List changedTiles) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (TryGetFarmTile(crop.TilePosition, out var ground)) { bool flag = false; Point tilePosition = crop.TilePosition; if ((int)ground == 3) { WorldTilesServerController.SetTile(tilePosition, (GroundType?)(GroundType)4, (StructureType?)null); flag = true; } ServerGameState.WetFarmlands[tilePosition] = status; if (!crop.IsWatered) { CropServerController.WaterCrop(crop); flag = true; } if (flag) { changedTiles.Add(tilePosition); } } } private static bool TryGetFarmTile(Point point, out GroundType ground) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected I4, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) ground = (GroundType)0; WorldTile[,] worldTiles = ServerGameState.WorldTiles; if (worldTiles == null || point.X < 0 || point.Y < 0 || point.X >= worldTiles.GetLength(0) || point.Y >= worldTiles.GetLength(1)) { return false; } ground = (GroundType)(int)worldTiles[point.X, point.Y].Ground; return (int)ground == 3 || (int)ground == 4; } private static void SendChangedTiles() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (ChangedTiles.Count > 0 && CanNotifyClients()) { CropServerService.SendWaterCrops(Vector2.Zero, ChangedTiles); } } private static bool CanUseWorldState() { return ServerGameState.WorldTiles != null && ServerGameState.Crops != null && ServerGameState.WetFarmlands != null; } private static bool CanNotifyClients() { return ServerRunState.OutsideWorld != null; } } [HarmonyPatch(typeof(CropServerSystem), "UpdateWetFarmlandCheck", new Type[] { typeof(float) })] internal static class WetFarmlandDryPatch { private static bool Prefix() { return !AlwaysWetFarmland.Enabled; } } [HarmonyPatch(typeof(CropServerSystem), "Update", new Type[] { typeof(GameTime) })] internal static class AlwaysWetFarmlandUpdatePatch { private static void Postfix() { AlwaysWetFarmland.Tick(); } } [HarmonyPatch(typeof(CropServerManager), "SpawnCrop", new Type[] { typeof(string), typeof(Point), typeof(bool), typeof(SyncStrategy) })] internal static class SpawnCropWaterSyncPatch { private static void Postfix(CropServerModel __result) { AlwaysWetFarmland.WetCropTile(__result, notify: true); } } [HarmonyPatch(typeof(CropServerController), "AfterWorldGameStateLoaded")] internal static class LoadedCropsWetFarmlandPatch { private static void Postfix() { AlwaysWetFarmland.MarkDirty(); } } [HarmonyPatch(typeof(BuildingSimulationModel), "Update", new Type[] { typeof(float) })] internal static class BuildingTickPatch { private static void Prefix(ref float dt) { if (PatchHelper.ShouldApply) { dt *= ConfigManager.BuildingTick; } } } [HarmonyPatch(typeof(CropServerSystem), "Update", new Type[] { typeof(GameTime) })] internal static class CropServerSystemPatch { private static void Prefix(out ConfigScaleState __state) { float original; bool changed = PatchHelper.TryScaleConfig(ConfigManager.CropGrowth, ConfigField.CropGrowthSpeedMultiplier, out original); __state = new ConfigScaleState(changed, original); } private static void Postfix(ConfigScaleState __state) { PatchHelper.RestoreConfig(ConfigField.CropGrowthSpeedMultiplier, __state.Original, __state.Changed); } private static Exception Finalizer(Exception __exception, ConfigScaleState __state) { PatchHelper.RestoreConfig(ConfigField.CropGrowthSpeedMultiplier, __state.Original, __state.Changed); return __exception; } } [HarmonyPatch(typeof(BuildingFurnaceHelper), "Tick", new Type[] { typeof(BuildingSimulationModel), typeof(SourceModel), typeof(bool) })] internal static class FuelPatch { private static void Prefix(out ConfigScaleState __state) { float original; bool changed = PatchHelper.TryScaleConfig(ConfigManager.FuelDrain, ConfigField.FuelDrainMultiplier, out original); __state = new ConfigScaleState(changed, original); } private static void Postfix(ConfigScaleState __state) { PatchHelper.RestoreConfig(ConfigField.FuelDrainMultiplier, __state.Original, __state.Changed); } private static Exception Finalizer(Exception __exception, ConfigScaleState __state) { PatchHelper.RestoreConfig(ConfigField.FuelDrainMultiplier, __state.Original, __state.Changed); return __exception; } } [HarmonyPatch(typeof(JobHelper), "GetJobTickForCurrentJob", new Type[] { typeof(CitizenModel), typeof(float) })] internal static class JobHelperPatch { private static void Postfix(ref float __result) { if (PatchHelper.ShouldApply) { __result *= ConfigManager.JobProgress; } } } internal static class PatchHelper { public static bool ShouldApply => ConfigManager.Active && IsServerRunning; private static bool IsServerRunning { get { BaseServer instance = BaseServer.Instance; return instance.Running || instance.Updating || ServerGlobals.StandAlone; } } public static bool TryScaleConfig(float multiplier, ConfigField field, out float original) { original = 1f; if (!ShouldApply || multiplier == 1f || ServerGameState.Config == null) { return false; } GameConfig config = ServerGameState.Config; original = Get(config, field); Set(config, field, original * multiplier); return true; } public static void RestoreConfig(ConfigField field, float original, bool changed) { if (changed && ServerGameState.Config != null) { Set(ServerGameState.Config, field, original); } } private static float Get(GameConfig config, ConfigField field) { return field switch { ConfigField.CropGrowthSpeedMultiplier => config.CropGrowthSpeedMultiplier, ConfigField.FuelDrainMultiplier => config.FuelDrainMultiplier, _ => 1f, }; } private static void Set(GameConfig config, ConfigField field, float value) { switch (field) { case ConfigField.CropGrowthSpeedMultiplier: config.CropGrowthSpeedMultiplier = value; break; case ConfigField.FuelDrainMultiplier: config.FuelDrainMultiplier = value; break; } } } internal enum ConfigField { CropGrowthSpeedMultiplier, FuelDrainMultiplier } internal readonly struct ConfigScaleState { public bool Changed { get; } public float Original { get; } public ConfigScaleState(bool changed, float original) { Changed = changed; Original = original; } } [HarmonyPatch(typeof(TradeRoutesHelper), "Server_GetRouteTime", new Type[] { typeof(BuildingInstanceModel), typeof(BuildingInstanceModel), typeof(CitizenModel), typeof(GameConfig), typeof(float) })] internal static class TradeRoutesPatch { private static void Postfix(ref float __result) { if (PatchHelper.ShouldApply) { __result /= ConfigManager.TradeRouteSpeed; } } } } namespace ProsperityTweaks.Localizations { internal static class LocalizationHelper { public static readonly string[] SupportedLanguages = new string[4] { "zh_CN", "zh_TW", "en", "ja_JP" }; public static Dictionary GetDefaultTranslations(string language) { Dictionary dictionary = new Dictionary(); switch (language) { case "zh_CN": dictionary.Add("mod.name", "繁荣调整"); dictionary.Add("mod.description", "调整农业、农田湿润、生产、燃料和贸易路线速度,让定居点发展更轻松。"); dictionary.Add("section.general", "通用"); dictionary.Add("section.production", "生产"); dictionary.Add("section.resources", "资源"); dictionary.Add("section.trade", "贸易"); dictionary.Add("entry.enabled", "启用模组"); dictionary.Add("entry.crop_growth", "作物成长倍率"); dictionary.Add("entry.always_wet_farmland", "永湿农田"); dictionary.Add("entry.building_tick", "建筑生产倍率"); dictionary.Add("entry.job_progress", "工作进度倍率"); dictionary.Add("entry.fuel_drain", "燃料消耗倍率"); dictionary.Add("entry.trade_route_speed", "贸易路线速度倍率"); dictionary.Add("config.enabled.description", "繁荣调整总开关。"); dictionary.Add("config.crop_growth.description", "额外作用于作物成长速度的倍率。"); dictionary.Add("config.always_wet_farmland.description", "作物所在的农田会自动保持湿润,并阻止湿农田自然变干。"); dictionary.Add("config.building_tick.description", "额外作用于建筑生产速度的倍率。"); dictionary.Add("config.job_progress.description", "额外作用于村民单次工作进度的倍率,会和建筑生产倍率叠加。"); dictionary.Add("config.fuel_drain.description", "额外作用于炉子燃料消耗的倍率。数值越低,燃料越耐烧。"); dictionary.Add("config.trade_route_speed.description", "额外作用于贸易路线速度的倍率。"); break; case "zh_TW": dictionary.Add("mod.name", "繁榮調整"); dictionary.Add("mod.description", "調整農業、農田濕潤、生產、燃料和貿易路線速度,讓聚落發展更輕鬆。"); dictionary.Add("section.general", "通用"); dictionary.Add("section.production", "生產"); dictionary.Add("section.resources", "資源"); dictionary.Add("section.trade", "貿易"); dictionary.Add("entry.enabled", "啟用模組"); dictionary.Add("entry.crop_growth", "作物成長倍率"); dictionary.Add("entry.always_wet_farmland", "永濕農田"); dictionary.Add("entry.building_tick", "建築生產倍率"); dictionary.Add("entry.job_progress", "工作進度倍率"); dictionary.Add("entry.fuel_drain", "燃料消耗倍率"); dictionary.Add("entry.trade_route_speed", "貿易路線速度倍率"); dictionary.Add("config.enabled.description", "繁榮調整總開關。"); dictionary.Add("config.crop_growth.description", "額外作用於作物成長速度的倍率。"); dictionary.Add("config.always_wet_farmland.description", "作物所在的農田會自動保持濕潤,並阻止濕農田自然變乾。"); dictionary.Add("config.building_tick.description", "額外作用於建築生產速度的倍率。"); dictionary.Add("config.job_progress.description", "額外作用於村民單次工作進度的倍率,會和建築生產倍率疊加。"); dictionary.Add("config.fuel_drain.description", "額外作用於爐子燃料消耗的倍率。數值越低,燃料越耐燒。"); dictionary.Add("config.trade_route_speed.description", "額外作用於貿易路線速度的倍率。"); break; case "ja_JP": dictionary.Add("mod.name", "Prosperity Tweaks"); dictionary.Add("mod.description", "農業、畑の湿り具合、生産、燃料、交易ルートの速度を調整し、集落の発展を楽にします。"); dictionary.Add("section.general", "全般"); dictionary.Add("section.production", "生産"); dictionary.Add("section.resources", "資源"); dictionary.Add("section.trade", "交易"); dictionary.Add("entry.enabled", "MODを有効化"); dictionary.Add("entry.crop_growth", "作物成長倍率"); dictionary.Add("entry.always_wet_farmland", "畑を常に湿らせる"); dictionary.Add("entry.building_tick", "建物生産倍率"); dictionary.Add("entry.job_progress", "作業進行倍率"); dictionary.Add("entry.fuel_drain", "燃料消費倍率"); dictionary.Add("entry.trade_route_speed", "交易ルート速度倍率"); dictionary.Add("config.enabled.description", "Prosperity Tweaks のマスタースイッチ。"); dictionary.Add("config.crop_growth.description", "作物の成長速度に追加で適用される倍率。"); dictionary.Add("config.always_wet_farmland.description", "作物が植えられている畑を自動的に湿らせ、湿った畑が自然に乾かないようにします。"); dictionary.Add("config.building_tick.description", "建物の作業速度に追加で適用される倍率。"); dictionary.Add("config.job_progress.description", "住民の 1 tick あたりの作業進行量に追加で適用される倍率。建物生産倍率と重なります。"); dictionary.Add("config.fuel_drain.description", "炉の燃料消費に追加で適用される倍率。低いほど燃料が長持ちします。"); dictionary.Add("config.trade_route_speed.description", "交易ルート速度に追加で適用される倍率。"); break; case "en": dictionary.Add("mod.name", "Prosperity Tweaks"); dictionary.Add("mod.description", "Tune farming, farmland wetness, production, fuel, and trade route speed to make settlement growth smoother."); dictionary.Add("section.general", "General"); dictionary.Add("section.production", "Production"); dictionary.Add("section.resources", "Resources"); dictionary.Add("section.trade", "Trade"); dictionary.Add("entry.enabled", "Enable Mod"); dictionary.Add("entry.crop_growth", "Crop Growth Multiplier"); dictionary.Add("entry.always_wet_farmland", "Always Wet Farmland"); dictionary.Add("entry.building_tick", "Building Production Multiplier"); dictionary.Add("entry.job_progress", "Job Progress Multiplier"); dictionary.Add("entry.fuel_drain", "Fuel Drain Multiplier"); dictionary.Add("entry.trade_route_speed", "Trade Route Speed Multiplier"); dictionary.Add("config.enabled.description", "Master switch for Prosperity Tweaks."); dictionary.Add("config.crop_growth.description", "Extra multiplier applied to crop growth speed."); dictionary.Add("config.always_wet_farmland.description", "Automatically keeps farmland with crops wet and prevents wet farmland from drying naturally."); dictionary.Add("config.building_tick.description", "Extra multiplier applied to building work speed."); dictionary.Add("config.job_progress.description", "Extra multiplier applied to citizen work progress per tick. This stacks with Building Production Multiplier."); dictionary.Add("config.fuel_drain.description", "Extra multiplier applied to furnace fuel drain. Lower values make fuel last longer."); dictionary.Add("config.trade_route_speed.description", "Extra multiplier applied to trade route speed."); break; } return dictionary; } } internal sealed class LocalizationManager { private static LocalizationManager _instance; private readonly Dictionary> _localizations = new Dictionary>(StringComparer.OrdinalIgnoreCase); private string _currentLocale = "en"; public static LocalizationManager Instance => _instance ?? (_instance = new LocalizationManager()); public string CurrentLocale => _currentLocale; public event Action LanguageChanged; private LocalizationManager() { Initialize(); } public void Initialize() { _localizations.Clear(); string[] supportedLanguages = LocalizationHelper.SupportedLanguages; foreach (string text in supportedLanguages) { Dictionary defaultTranslations = LocalizationHelper.GetDefaultTranslations(text); if (defaultTranslations.Count > 0) { _localizations[text] = defaultTranslations; } } RefreshLanguage(forceNotify: false); } public void RefreshLanguage(bool forceNotify = true) { string text = DetectLocale(); if (!_localizations.ContainsKey(text)) { text = "en"; } bool flag = !string.Equals(_currentLocale, text, StringComparison.OrdinalIgnoreCase); _currentLocale = text; if (flag || forceNotify) { this.LanguageChanged?.Invoke(); } } public string GetText(string key, params object[] args) { RefreshLanguage(forceNotify: false); string text = TryGetText(_currentLocale, key) ?? TryGetText("en", key) ?? TryGetText("zh_CN", key) ?? key; return (args != null && args.Length != 0) ? string.Format(text, args) : text; } private string TryGetText(string locale, string key) { Dictionary value; string value2; return (_localizations.TryGetValue(locale, out value) && value.TryGetValue(key, out value2)) ? value2 : null; } private static string DetectLocale() { if (!string.IsNullOrWhiteSpace(Globals.LanguageCode)) { return NormalizeLocale(Globals.LanguageCode); } string text = ReadSavedLocale(); if (!string.IsNullOrWhiteSpace(text)) { return NormalizeLocale(text); } return NormalizeLocale(CultureInfo.CurrentCulture.Name); } private static string ReadSavedLocale() { try { string path = Path.Combine(GamePathHelper.GetFolderPath((GameFolder)1, false), "localization.json"); if (!File.Exists(path)) { return null; } using FileStream utf8Json = File.OpenRead(path); using JsonDocument jsonDocument = JsonDocument.Parse((Stream)utf8Json, new JsonDocumentOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); JsonElement value; return (jsonDocument.RootElement.TryGetProperty("LanguageCode", out value) && value.ValueKind == JsonValueKind.String) ? value.GetString() : null; } catch { return null; } } private static string NormalizeLocale(string languageCode) { switch ((languageCode ?? string.Empty).Replace('-', '_')) { case "zh_CN": case "zh_Hans": return "zh_CN"; case "zh_TW": case "zh_HK": case "zh_MO": case "zh_Hant": return "zh_TW"; case "ja_JP": case "ja": return "ja_JP"; case "en": case "en_US": case "en_GB": return "en"; default: return "en"; } } } } namespace ProsperityTweaks.Configuration { internal static class ConfigManager { public const string GeneralSection = "General"; public const string ProductionSection = "Production"; public const string ResourcesSection = "Resources"; public const string TradeSection = "Trade"; public static ConfigEntry Enabled { get; private set; } public static ConfigEntry CropGrowthMultiplier { get; private set; } public static ConfigEntry AlwaysWetFarmland { get; private set; } public static ConfigEntry BuildingTickMultiplier { get; private set; } public static ConfigEntry JobProgressMultiplier { get; private set; } public static ConfigEntry FuelDrainMultiplier { get; private set; } public static ConfigEntry TradeRouteSpeedMultiplier { get; private set; } public static float CropGrowth => GetMultiplier(CropGrowthMultiplier); public static bool KeepFarmlandWet => AlwaysWetFarmland?.Value ?? false; public static float BuildingTick => GetMultiplier(BuildingTickMultiplier); public static float JobProgress => GetMultiplier(JobProgressMultiplier); public static float FuelDrain => GetMultiplier(FuelDrainMultiplier); public static float TradeRouteSpeed => GetMultiplier(TradeRouteSpeedMultiplier); public static bool Active => Enabled?.Value ?? false; public static void Init(ConfigFile config) { Enabled = config.Bind("General", "Enabled", true, Text("config.enabled.description")); CropGrowthMultiplier = config.Bind("Production", "CropGrowthMultiplier", 1f, Range("config.crop_growth.description", 0.1f, 50f)); AlwaysWetFarmland = config.Bind("Production", "AlwaysWetFarmland", false, Text("config.always_wet_farmland.description")); BuildingTickMultiplier = config.Bind("Production", "BuildingTickMultiplier", 1f, Range("config.building_tick.description", 0.1f, 50f)); JobProgressMultiplier = config.Bind("Production", "JobProgressMultiplier", 1f, Range("config.job_progress.description", 0.1f, 50f)); FuelDrainMultiplier = config.Bind("Resources", "FuelDrainMultiplier", 1f, Range("config.fuel_drain.description", 0f, 1f)); TradeRouteSpeedMultiplier = config.Bind("Trade", "TradeRouteSpeedMultiplier", 1f, Range("config.trade_route_speed.description", 0.1f, 50f)); } private static ConfigDescription Range(string descriptionKey, float min, float max) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return new ConfigDescription(Text(descriptionKey), (AcceptableValueBase)(object)new AcceptableValueRange(min, max), Array.Empty()); } private static float GetMultiplier(ConfigEntry entry) { return entry?.Value ?? 1f; } private static string Text(string key) { return LocalizationManager.Instance.GetText(key); } } internal static class ModSettingsMenuRegistration { private static ConfigFile _config; private static string _locale; private static bool _subscribed; public static void Register(ConfigFile config) { _config = config; if (!_subscribed) { ModSettingsRegistry.RefreshRequested += Refresh; _subscribed = true; } RegisterCurrent(config); } public static void Refresh() { if (_config != null) { LocalizationManager.Instance.RefreshLanguage(forceNotify: false); string currentLocale = LocalizationManager.Instance.CurrentLocale; ModSettingsRegistration val = default(ModSettingsRegistration); if (!(_locale == currentLocale) || !ModSettingsRegistry.TryGet("IceBoxStudio.Romestead.ProsperityTweaks", ref val)) { RegisterCurrent(_config); } } } private static void RegisterCurrent(ConfigFile config) { ModSettingsRegistry.Register("IceBoxStudio.Romestead.ProsperityTweaks", Text("mod.name"), config, (Action)delegate(ModSettingsRegistration registration) { registration.Icon = "cog"; registration.Version = "1.0.0"; registration.Author = "Ice Box Studio"; registration.Description = Text("mod.description"); registration.NexusModsId = 22; registration.UpdateManifestUrl = "https://fastly.jsdelivr.net/gh/ibox233/MyMod_Version@main/ProsperityTweaks.json"; registration.Order = -80; registration.ConfigureSection("General", (Action)delegate(ModSettingsSectionOptions section) { section.DisplayName = Text("section.general"); section.Order = 10; }); registration.ConfigureSection("Production", (Action)delegate(ModSettingsSectionOptions section) { section.DisplayName = Text("section.production"); section.Order = 20; }); registration.ConfigureSection("Resources", (Action)delegate(ModSettingsSectionOptions section) { section.DisplayName = Text("section.resources"); section.Order = 30; }); registration.ConfigureSection("Trade", (Action)delegate(ModSettingsSectionOptions section) { section.DisplayName = Text("section.trade"); section.Order = 40; }); registration.ConfigureEntry("General", "Enabled", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.enabled"); entry.Description = Text("config.enabled.description"); entry.Order = 1; }); registration.ConfigureEntry("Production", "CropGrowthMultiplier", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.crop_growth"); entry.Description = Text("config.crop_growth.description"); entry.SliderStep = 0.1; entry.Order = 1; }); registration.ConfigureEntry("Production", "AlwaysWetFarmland", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.always_wet_farmland"); entry.Description = Text("config.always_wet_farmland.description"); entry.Order = 2; }); registration.ConfigureEntry("Production", "BuildingTickMultiplier", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.building_tick"); entry.Description = Text("config.building_tick.description"); entry.SliderStep = 0.1; entry.Order = 3; }); registration.ConfigureEntry("Production", "JobProgressMultiplier", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.job_progress"); entry.Description = Text("config.job_progress.description"); entry.SliderStep = 0.1; entry.Order = 4; }); registration.ConfigureEntry("Resources", "FuelDrainMultiplier", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.fuel_drain"); entry.Description = Text("config.fuel_drain.description"); entry.SliderStep = 0.1; entry.Order = 1; }); registration.ConfigureEntry("Trade", "TradeRouteSpeedMultiplier", (Action)delegate(ModSettingsEntryOptions entry) { entry.DisplayName = Text("entry.trade_route_speed"); entry.Description = Text("config.trade_route_speed.description"); entry.SliderStep = 0.1; entry.Order = 1; }); }); _locale = LocalizationManager.Instance.CurrentLocale; } private static string Text(string key) { return LocalizationManager.Instance.GetText(key); } } }