Decompiled source of RomesteadContentFramework v1.0.0
BepInEx/plugins/RomesteadContentFramework.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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.Xml; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.NET.Common; using Candide; using Candide.Combat; using Candide.Data; using Candide.Database; using Candide.Database.Doodad; using Candide.GameModels; using Candide.GameModels.Controllers; using Candide.GameModels.Managers; using Candide.GameModels.Models.Spells; using Candide.Graphics.VFX; using Candide.World; using CandideCreator.Shared; using CandideCreator.Shared.Graphics; using CandideCreator.Shared.Helpers; using CandideCreator.Shared.Models; using CandideCreator.Shared.Models.VFX; using CandideCreator.Shared.Serialization; using CandideCreator.Shared.Storage; using CandideCreator.Shared.Tiled; using CandideServer; using CandideServer.Buildings.BuildingControllers; using CandideServer.Citizens; using CandideServer.Citizens.Base; using CandideServer.Data.DataModels; using CandideServer.Data.ServerDataSetups; using CandideServer.Data.SharedDataSetups; using CandideServer.DataSystem; using CandideServer.Database; using CandideServer.Entities; using CandideServer.Helpers; using CandideServer.Models; using CandideServer.Models.Buildings; using CandideServer.Models.Citizen; using CandideServer.Models.Spells; using CandideServer.Models.WorkOrders; using CandideServer.Saving; using CandideServer.ServerControllers; using CandideServer.ServerManagers; using CandideServer.ServerSystems; using CandideServer.SimulationModels; using CandideServer.Simulations; using CandideServer.World; using CandideServer.World.Generation; using HarmonyLib; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using RomesteadContentFramework.Api; using RomesteadContentFramework.Runtime; using Shared.Collision; using Shared.Combat.Projectiles; using Shared.Combat.Spells; using Shared.Combat.Spells.Casting; using Shared.Combat.Spells.Parameters; using Shared.Data; using Shared.Data.DataModels; using Shared.Data.Decorations; using Shared.Data.Furniture; using Shared.Entity; using Shared.Entity.Components; using Shared.Helpers; using Shared.Messages; using Shared.Models; using Shared.Models.Buildings; using Shared.Models.Construction; using Shared.Models.Crafting; using Shared.Models.DropTables; using Shared.Models.Items; using Shared.Models.Shops; using Shared.Models.Spells; using Shared.Text; using Shared.World; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Romestead Content Framework")] [assembly: AssemblyDescription("Romestead Content Framework For Romestead by Ice Box Studio")] [assembly: AssemblyCompany("Ice Box Studio")] [assembly: AssemblyProduct("Romestead Content Framework")] [assembly: AssemblyCopyright("Copyright (C) 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 RomesteadContentFramework { public static class PluginInfo { public const string PLUGIN_GUID = "IceBoxStudio.Romestead.RomesteadContentFramework"; public const string PLUGIN_NAME = "Romestead Content Framework"; 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.RomesteadContentFramework", "Romestead Content Framework", "1.0.0")] public sealed class RomesteadContentFramework : BasePlugin { private Harmony _harmony; public static RomesteadContentFramework Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown Instance = this; Logger = ((BasePlugin)this).Log; bool flag = default(bool); try { _harmony = new Harmony("IceBoxStudio.Romestead.RomesteadContentFramework"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); RomesteadContent.ApplyAll(); ManualLogSource logger = Logger; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Romestead Content Framework"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initialized."); } logger.LogInfo(val); } catch (Exception ex) { ManualLogSource logger2 = Logger; BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(24, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("Romestead Content Framework"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" initialization error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.StackTrace); } logger2.LogError(val2); } } } } namespace RomesteadContentFramework.Runtime { public sealed class StandardBuildingController : AbstractBuildingController { public StandardBuildingController(BuildingSimulationModel building) : base(building, true) { base.JobTickRate = (RomesteadContent.TryGetStandardWorkerSettings(this, out var jobTickRate, out var _) ? jobTickRate : 0.5f); } public override void OnNewlyCreated() { RomesteadContent.StandardBuildingCreated(this); } public override void OnLoaded() { RomesteadContent.StandardBuildingLoaded(this); } public override void OnBuildingRemove() { RomesteadContent.StandardBuildingRemoved(this); ((AbstractBuildingController)this).OnBuildingRemove(); } public override void OnUpgraded(ConstructionModel upgradeConstructionModel, BuildingData upgradedBuildingData) { RomesteadContent.StandardBuildingUpgraded(this, upgradeConstructionModel, upgradedBuildingData); } public override void Update(float dt) { if (RomesteadContent.TryGetStandardWorkerSettings(this, out var jobTickRate, out var _)) { base.JobTickRate = jobTickRate; ((AbstractBuildingController)this).Update(dt); } } protected override void OnTickJob(CitizenModel citizen, JobModel job) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 if (!RomesteadContent.TryGetStandardWorkerSettings(this, out var _, out var qualityModifier)) { return; } WorkOrderModel val = JobServerManager.ApplyCurrentOrderProgress(job, JobHelper.GetJobTickForCurrentJob(citizen, base.JobTickRate), JobHelper.GetJobQualityValueTick(citizen, job, qualityModifier)); if (!(val.CurrentProgress < val.RequiredProgress)) { WorkOrderType type = val.Output.Type; if ((int)type == 0 || type - 3 <= 1) { ((AbstractBuildingController)this).DefaultFinishCurrentWorkOrderHandle(job, citizen); } else { RomesteadContent.WarnUnsupportedStandardWorkOrder(this, ((object)Unsafe.As<WorkOrderType, WorkOrderType>(ref val.Output.Type)/*cast due to .constrained prefix*/).ToString()); } } } } } namespace RomesteadContentFramework.Patches { [HarmonyPatch(typeof(ClientDataSetup), "SetupClientData")] internal static class ClientDataSetupPatch { private static void Postfix() { RomesteadContent.ApplyClientContent(); } } [HarmonyPatch(typeof(Content), "TryLoadBackUpTexture")] internal static class ContentTryLoadBackUpTexturePatch { private static void Postfix(string path, ref Texture2D __result) { if (__result == null && RomesteadContent.TryLoadRegisteredLooseTexture(path, out var texture)) { __result = texture; } } } [HarmonyPatch(typeof(GameState), "SetupNewGameState")] internal static class GameStateSetupNewGameStatePatch { private static void Postfix() { RomesteadContent.ApplyPlayerRecipeUnlocks(); } } [HarmonyPatch(typeof(WorldFlagsController), "SynchWorldFlagChanged")] internal static class WorldFlagsControllerSynchWorldFlagChangedPatch { private static void Postfix() { RomesteadContent.ApplyPlayerRecipeUnlocks(); } } [HarmonyPatch(typeof(WorldFlagsController), "SyncFullGameState")] internal static class WorldFlagsControllerSyncFullGameStatePatch { private static void Postfix() { RomesteadContent.ApplyPlayerRecipeUnlocks(); } } [HarmonyPatch(typeof(CitizenControllerDataBase), "GetControllerOrNullForJob", new Type[] { typeof(string) })] internal static class CustomJobCitizenControllerPatch { private static void Postfix(string __0, ref AbstractCitizenController __result) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (__result == null && RomesteadContent.IsRegisteredJob(__0)) { __result = (AbstractCitizenController)new GenericServerCitizenController(); } } } [HarmonyPatch(typeof(GenericServerCitizenController), "HasActiveWorkOrder", new Type[] { typeof(CitizenSlotModel) })] internal static class GenericServerCitizenControllerPatch { private static void Postfix(CitizenSlotModel __0, ref bool __result) { if (!__result && __0.AssociatedJobId.HasValue && ServerGameState.Jobs.TryGetValue(__0.AssociatedJobId.Value, out var value) && RomesteadContent.IsRegisteredJob(value.BaseId)) { string text = value.CurrentWorkOrder?.RecipeId; if (!string.IsNullOrEmpty(text) && !(text == "recipe:constructing") && !(text == "recipe:repairing")) { __result = true; } } } } [HarmonyPatch(typeof(ItemRecipeManager), "GetLocalPlayerRecipesForStation")] internal static class ItemRecipeManagerGetLocalPlayerRecipesForStationPatch { private static void Postfix(HashSet<string> list, string[] stationIds) { IReadOnlyList<ItemRecipe> unlocked = RomesteadContent.Recipes.GetUnlocked(); for (int i = 0; i < unlocked.Count; i++) { ItemRecipe val = unlocked[i]; if (val != null && MatchesStation(val, stationIds)) { list.Add(val.Id); } } } private static bool MatchesStation(ItemRecipe recipe, string[] stationIds) { if (stationIds == null) { return string.IsNullOrWhiteSpace(recipe.RequiredCraftingStation); } for (int i = 0; i < stationIds.Length; i++) { if (stationIds[i] == recipe.RequiredCraftingStation) { return true; } } return false; } } [HarmonyPatch(typeof(ServerDataSetup), "Setup")] internal static class ServerDataSetupPatch { private static void Postfix() { RomesteadContent.ApplyServerContent(); } } [HarmonyPatch(typeof(ServerWorldDataManager), "LoadMapFiles", new Type[] { })] internal static class ServerWorldDataManagerLoadMapFilesPatch { private static void Postfix() { RomesteadContent.MarkServerMapsReady(); RomesteadContent.ApplyMaps(); } } [HarmonyPatch(typeof(ServerWorldDataManager), "GetWorldDataOrNull")] internal static class ServerWorldDataManagerGetWorldDataOrNullPatch { private static void Postfix(string mapName, ref ServerWorldData __result) { if (__result == null && RomesteadContent.TryGetFallbackServerWorldData(mapName, out var worldData)) { __result = worldData; } } } [HarmonyPatch(typeof(GameSaveManager), "LoadGame")] internal static class GameSaveManagerLoadGamePatch { private static void Postfix() { RomesteadContent.ApplyServerContent(); } } [HarmonyPatch(typeof(SharedDataSetup), "Setup")] internal static class SharedDataSetupSetupPatch { private static void Postfix() { RomesteadContent.ApplySharedContent(); } } [HarmonyPatch(typeof(SharedDataSetup), "SharedSetup")] internal static class ServerSharedDataSetupPatch { private static void Postfix() { RomesteadContent.ApplyServerSharedContent(); } } [HarmonyPatch(typeof(LocalizationManager), "SetLocalization")] internal static class LocalizationManagerSetLocalizationPatch { private static void Postfix() { RomesteadContent.ApplyLocalization(); } } [HarmonyPatch(typeof(TownServerSystem), "OnNewDay")] internal static class StandardBuildingBehaviorNewDayPatch { private static void Postfix() { RomesteadContent.HandleStandardBuildingNewDay(); } } [HarmonyPatch(typeof(WorldLoader), "LoadWorld")] internal static class WorldLoaderPatch { private static bool Prefix(string mapName, ref World __result) { if (!RomesteadContent.TryLoadClientWorld(mapName, out var world)) { return true; } __result = world; return false; } } } namespace RomesteadContentFramework.Api { public static class RomesteadContent { public sealed class BuildingContentRegistry { public void RegisterStandardType(string id, string name, Action<StandardBuildingBehaviorOptions> configure) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Building type id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } StandardBuildingBehaviorOptions standardBuildingBehaviorOptions = new StandardBuildingBehaviorOptions(); configure(standardBuildingBehaviorOptions); standardBuildingBehaviorOptions.Validate(id); RegistrationSource registrationSource = GetRegistrationSource(); lock (Sync) { StandardBuildingBehaviors[id] = new StandardBuildingBehaviorRegistration(id, standardBuildingBehaviorOptions.Clone(), registrationSource); } RegisterType(new BuildingTypeData { Id = id, Name = StringId.op_Implicit(id + "*building_type:name"), CreateController = (BuildingSimulationModel building) => (AbstractBuildingController)(object)new StandardBuildingController(building) }, name); } public void RegisterConstructionGroup(ConstructionsGroupData group) { if (group == null) { throw new ArgumentNullException("group"); } if (string.IsNullOrWhiteSpace(group.Id)) { throw new ArgumentException("Construction group id cannot be empty.", "group"); } if (group.ConstructionIds == null) { throw new ArgumentException("Construction group must define ConstructionIds.", "group"); } lock (Sync) { ConstructionGroupRegistrations[group.Id] = new ConstructionGroupRegistration(group); } if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterConstructionResource(ConstructionResourceDataModel resource, string name = null, string description = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(resource.Id)) { throw new ArgumentException("Construction resource id cannot be empty.", "resource"); } lock (Sync) { ConstructionResourceRegistrations[resource.Id] = new ConstructionResourceRegistration(resource); if (!string.IsNullOrEmpty(name)) { Strings[ConstructionResourceNameKey(resource.Id)] = name; } if (!string.IsNullOrEmpty(description)) { Strings[ConstructionResourceDescriptionKey(resource.Id)] = description; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterCraftingStation(CraftingStationData station, string name = null) { if (station == null) { throw new ArgumentNullException("station"); } if (string.IsNullOrWhiteSpace(station.Id)) { throw new ArgumentException("Crafting station id cannot be empty.", "station"); } lock (Sync) { CraftingStationRegistrations[station.Id] = new CraftingStationRegistration(station); if (!string.IsNullOrEmpty(name)) { Strings[CraftingStationNameKey(station.Id)] = name; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterDecoration(DecorationData decoration) { if (decoration == null) { throw new ArgumentNullException("decoration"); } if (string.IsNullOrWhiteSpace(decoration.Id)) { throw new ArgumentException("Decoration id cannot be empty.", "decoration"); } lock (Sync) { DecorationRegistrations[decoration.Id] = new DecorationRegistration(decoration); } if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterFurniture(FurnitureData furniture, string name = null, string description = null) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (furniture == null) { throw new ArgumentNullException("furniture"); } if (string.IsNullOrWhiteSpace(furniture.Id)) { throw new ArgumentException("Furniture id cannot be empty.", "furniture"); } if (furniture.Rotations == null) { throw new ArgumentException("Furniture must define Rotations.", "furniture"); } lock (Sync) { FurnitureRegistrations[furniture.Id] = new FurnitureRegistration(furniture); if (!string.IsNullOrEmpty(name)) { Strings[FurnitureNameKey(furniture.Id)] = name; } if (!string.IsNullOrEmpty(description)) { furniture.DescriptionKey = StringId.op_Implicit(FurnitureDescriptionKey(furniture.Id)); Strings[FurnitureDescriptionKey(furniture.Id)] = description; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterType(BuildingTypeData buildingType, string name = null) { if (buildingType == null) { throw new ArgumentNullException("buildingType"); } if (string.IsNullOrWhiteSpace(buildingType.Id)) { throw new ArgumentException("Building type id cannot be empty.", "buildingType"); } lock (Sync) { BuildingTypeRegistrations[buildingType.Id] = new BuildingTypeRegistration(buildingType, name); if (!string.IsNullOrEmpty(name)) { Strings[BuildingTypeNameKey(buildingType.Id)] = name; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterBuilding(BuildingData building) { if (building == null) { throw new ArgumentNullException("building"); } if (string.IsNullOrWhiteSpace(building.Id)) { throw new ArgumentException("Building id cannot be empty.", "building"); } if (string.IsNullOrWhiteSpace(building.BuildingTypeId)) { throw new ArgumentException("Building type id cannot be empty.", "building"); } ApplyStandardBuildingDefaults(building); lock (Sync) { BuildingRegistrations[building.Id] = new BuildingRegistration(building); } if (IsSharedContentReady()) { ApplyBuildings(); } } public void RegisterConstruction(ConstructionModel construction, string name = null, string description = null, bool unlockedByDefault = true) { RegisterConstruction(construction, delegate(ConstructionRegistrationOptions options) { options.Name = name; options.Description = description; options.UnlockedByDefault = unlockedByDefault; }); } public void RegisterConstruction(ConstructionModel construction, Action<ConstructionRegistrationOptions> configure) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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) if (construction == null) { throw new ArgumentNullException("construction"); } if (configure == null) { throw new ArgumentNullException("configure"); } if (string.IsNullOrWhiteSpace(construction.Id)) { throw new ArgumentException("Construction id cannot be empty.", "construction"); } ConstructionRegistrationOptions constructionRegistrationOptions = new ConstructionRegistrationOptions(); configure(constructionRegistrationOptions); ValidateConstruction(construction); lock (Sync) { ConstructionRegistrations[construction.Id] = new ConstructionRegistration(construction, constructionRegistrationOptions); if (!string.IsNullOrEmpty(constructionRegistrationOptions.Name)) { construction.Name = StringId.op_Implicit(ConstructionNameKey(construction.Id)); Strings[ConstructionNameKey(construction.Id)] = constructionRegistrationOptions.Name; } if (!string.IsNullOrEmpty(constructionRegistrationOptions.Description)) { construction.Description = StringId.op_Implicit(ConstructionDescriptionKey(construction.Id)); Strings[ConstructionDescriptionKey(construction.Id)] = constructionRegistrationOptions.Description; } if (constructionRegistrationOptions.UnlockedByDefault) { DefaultUnlockedConstructionIds.Add(construction.Id); } else { DefaultUnlockedConstructionIds.Remove(construction.Id); } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyBuildings(); } if (IsServerContentReady()) { ApplyConstructionUnlocks(); } } public void UnlockConstruction(string constructionId) { if (string.IsNullOrWhiteSpace(constructionId)) { throw new ArgumentException("Construction id cannot be empty.", "constructionId"); } lock (Sync) { DefaultUnlockedConstructionIds.Add(constructionId); } if (IsServerContentReady()) { ApplyConstructionUnlocks(); } } public void LockConstruction(string constructionId) { if (string.IsNullOrWhiteSpace(constructionId)) { throw new ArgumentException("Construction id cannot be empty.", "constructionId"); } lock (Sync) { DefaultUnlockedConstructionIds.Remove(constructionId); } } } public sealed class StandardBuildingBehaviorOptions { private readonly List<StandardItemProduction> productions = new List<StandardItemProduction>(); internal bool HasStorageInventory { get; private set; } internal bool HasOutputInventory { get; private set; } internal ItemFlag StorageItemFlags { get; private set; } internal ItemFlag OutputItemFlags { get; private set; } public BuildingLogisticsConfig? InputLogistics { get; set; } public BuildingLogisticsConfig? OutputLogistics { get; set; } public StandardBuildingProductionMode ProductionMode { get; private set; } public float WorkerJobTickRate { get; private set; } = 0.5f; public float WorkerQualityModifier { get; private set; } = -0.5f; public Action<StandardBuildingContext> OnCreated { get; set; } public Action<StandardBuildingContext> OnLoaded { get; set; } public Action<StandardBuildingContext> OnNewDay { get; set; } public Action<StandardBuildingUpgradeContext> OnUpgraded { get; set; } public Action<StandardBuildingContext> OnRemoved { get; set; } internal IReadOnlyList<StandardItemProduction> Productions => productions; public void CreateStorageInventory(ItemFlag acceptedItemFlags = (ItemFlag)0) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) HasStorageInventory = true; StorageItemFlags = acceptedItemFlags; if (!InputLogistics.HasValue) { InputLogistics = BuildingLogisticsConfig.CraftingBuildingDefaultInput; } } public void CreateOutputInventory(ItemFlag acceptedItemFlags = (ItemFlag)0) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) HasOutputInventory = true; OutputItemFlags = acceptedItemFlags; if (!OutputLogistics.HasValue) { OutputLogistics = BuildingLogisticsConfig.ResourceAndItemProducingDefaultOutput; } } public void ProduceItemDaily(string itemId, int amount = 1, int firstDayDelay = 1) { ProduceItemEveryDays(itemId, amount, 1, firstDayDelay); } public void ProduceItemEveryDays(string itemId, int amount, int everyDays, int firstDayDelay = 1) { if (ProductionMode == StandardBuildingProductionMode.WorkerJob) { throw new InvalidOperationException("Worker-job production and automatic timed production cannot be configured on the same standard building type."); } if (string.IsNullOrWhiteSpace(itemId)) { throw new ArgumentException("Produced item id cannot be empty.", "itemId"); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount", "Produced item amount must be greater than zero."); } if (everyDays <= 0) { throw new ArgumentOutOfRangeException("everyDays", "Production interval must be greater than zero."); } if (firstDayDelay < 0) { throw new ArgumentOutOfRangeException("firstDayDelay", "First production delay cannot be negative."); } ProductionMode = StandardBuildingProductionMode.AutomaticDaily; if (!HasOutputInventory) { CreateOutputInventory((ItemFlag)0); } productions.Add(new StandardItemProduction(itemId, amount, everyDays, firstDayDelay)); } public void UseWorkerJobs(float jobTickRate = 0.5f, float qualityModifier = -0.5f) { if (productions.Count > 0 || ProductionMode == StandardBuildingProductionMode.AutomaticDaily) { throw new InvalidOperationException("Automatic timed production and worker-job production cannot be configured on the same standard building type."); } if (jobTickRate <= 0f) { throw new ArgumentOutOfRangeException("jobTickRate", "Worker job tick rate must be greater than zero."); } ProductionMode = StandardBuildingProductionMode.WorkerJob; WorkerJobTickRate = jobTickRate; WorkerQualityModifier = qualityModifier; if (!HasStorageInventory) { CreateStorageInventory((ItemFlag)0); } if (!HasOutputInventory) { CreateOutputInventory((ItemFlag)0); } } internal void Validate(string buildingTypeId) { if (productions.Count > 0 && !HasOutputInventory) { throw new InvalidOperationException("Standard building type '" + buildingTypeId + "' produces items but has no output inventory."); } } internal StandardBuildingBehaviorOptions Clone() { //IL_001f: 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) StandardBuildingBehaviorOptions standardBuildingBehaviorOptions = new StandardBuildingBehaviorOptions(); standardBuildingBehaviorOptions.HasStorageInventory = HasStorageInventory; standardBuildingBehaviorOptions.HasOutputInventory = HasOutputInventory; standardBuildingBehaviorOptions.StorageItemFlags = StorageItemFlags; standardBuildingBehaviorOptions.OutputItemFlags = OutputItemFlags; standardBuildingBehaviorOptions.InputLogistics = InputLogistics; standardBuildingBehaviorOptions.OutputLogistics = OutputLogistics; standardBuildingBehaviorOptions.ProductionMode = ProductionMode; standardBuildingBehaviorOptions.WorkerJobTickRate = WorkerJobTickRate; standardBuildingBehaviorOptions.WorkerQualityModifier = WorkerQualityModifier; standardBuildingBehaviorOptions.OnCreated = OnCreated; standardBuildingBehaviorOptions.OnLoaded = OnLoaded; standardBuildingBehaviorOptions.OnNewDay = OnNewDay; standardBuildingBehaviorOptions.OnUpgraded = OnUpgraded; standardBuildingBehaviorOptions.OnRemoved = OnRemoved; standardBuildingBehaviorOptions.productions.AddRange(productions); return standardBuildingBehaviorOptions; } } public enum StandardBuildingProductionMode { None, AutomaticDaily, WorkerJob } public sealed class StandardBuildingContext { public BuildingSimulationModel Building { get; } public BuildingInstanceModel Instance => Building.InstanceModel; public int Day => TimeServerSystem.Day; public int UpgradeLevel => ConstructionDataBase.GetConstructionOrMissing(Instance.ConstructionId).UpgradeLevel; public SharedInventoryModel StorageInventory { get { SharedInventoryModel result = default(SharedInventoryModel); if (!Instance.StorageInventoryId.HasValue || !ServerGameState.TryGetInventory(Instance.StorageInventoryId.Value, ref result)) { return null; } return result; } } public SharedInventoryModel OutputInventory { get { SharedInventoryModel result = default(SharedInventoryModel); if (!Instance.OutputInventoryId.HasValue || !ServerGameState.TryGetInventory(Instance.OutputInventoryId.Value, ref result)) { return null; } return result; } } internal StandardBuildingContext(BuildingSimulationModel building) { Building = building; } public bool TryAddOutputItem(string itemId, int amount, string auraId = null) { if (string.IsNullOrWhiteSpace(itemId)) { throw new ArgumentException("Item id cannot be empty.", "itemId"); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount", "Item amount must be greater than zero."); } if (!Instance.OutputInventoryId.HasValue || ItemDataBase.GetItemDataOrNull(itemId) == null) { return false; } return SimpleInventoryServerManager.TryAddItemAmounts(Instance.OutputInventoryId.Value, itemId, amount, auraId, false, (int?)null, true); } public string GetState(string key, string fallback = null) { ValidateStateKey(key); if (Instance.Parameters == null || !Instance.Parameters.TryGetValue("romestead_content_framework:state:" + key, out var value)) { return fallback; } return value; } public void SetState(string key, string value) { ValidateStateKey(key); if (value == null) { throw new ArgumentNullException("value"); } EnsureParameters(Instance)["romestead_content_framework:state:" + key] = value; } public bool RemoveState(string key) { ValidateStateKey(key); if (Instance.Parameters != null) { return Instance.Parameters.Remove("romestead_content_framework:state:" + key); } return false; } private static void ValidateStateKey(string key) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("State key cannot be empty.", "key"); } } } public sealed class StandardBuildingUpgradeContext { public StandardBuildingContext Building { get; } public ConstructionModel UpgradeConstruction { get; } public BuildingData UpgradedBuildingData { get; } internal StandardBuildingUpgradeContext(BuildingSimulationModel building, ConstructionModel upgradeConstruction, BuildingData upgradedBuildingData) { Building = new StandardBuildingContext(building); UpgradeConstruction = upgradeConstruction; UpgradedBuildingData = upgradedBuildingData; } } private sealed class StandardBuildingBehaviorRegistration { public string Id { get; } public StandardBuildingBehaviorOptions Options { get; } public RegistrationSource Source { get; } public StandardBuildingBehaviorRegistration(string id, StandardBuildingBehaviorOptions options, RegistrationSource source) { Id = id; Options = options; Source = source; } } internal sealed class StandardItemProduction { public string ItemId { get; } public int Amount { get; } public int EveryDays { get; } public int FirstDayDelay { get; } public StandardItemProduction(string itemId, int amount, int everyDays, int firstDayDelay) { ItemId = itemId; Amount = amount; EveryDays = everyDays; FirstDayDelay = firstDayDelay; } } private sealed class ConstructionGroupRegistration { public ConstructionsGroupData Group { get; } public ConstructionGroupRegistration(ConstructionsGroupData group) { Group = group; } } private sealed class ConstructionResourceRegistration { public ConstructionResourceDataModel Resource { get; } public ConstructionResourceRegistration(ConstructionResourceDataModel resource) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) Resource = resource; } } private sealed class CraftingStationRegistration { public CraftingStationData Station { get; } public CraftingStationRegistration(CraftingStationData station) { Station = station; } } private sealed class DecorationRegistration { public DecorationData Decoration { get; } public DecorationRegistration(DecorationData decoration) { Decoration = decoration; } } private sealed class FurnitureRegistration { public FurnitureData Furniture { get; } public FurnitureRegistration(FurnitureData furniture) { Furniture = furniture; } } public sealed class ConstructionRegistrationOptions { public string Name { get; set; } public string Description { get; set; } public bool UnlockedByDefault { get; set; } = true; } private sealed class BuildingTypeRegistration { public BuildingTypeData BuildingType { get; } public string Name { get; } public BuildingTypeRegistration(BuildingTypeData buildingType, string name) { BuildingType = buildingType; Name = name; } } private sealed class BuildingRegistration { public BuildingData Building { get; } public BuildingRegistration(BuildingData building) { Building = building; } } private sealed class ConstructionRegistration { public ConstructionModel Construction { get; } public string Name { get; } public string Description { get; } public bool UnlockedByDefault { get; } public ConstructionRegistration(ConstructionModel construction, ConstructionRegistrationOptions options) { Construction = construction; Name = options.Name; Description = options.Description; UnlockedByDefault = options.UnlockedByDefault; } } public sealed class DropTableContentRegistry { public void Register(string id, DropTableModel model) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Drop table id cannot be empty.", "id"); } if (model == null) { throw new ArgumentNullException("model"); } lock (Sync) { DropTables[id] = new DropTableRegistration(id, model); } if (IsServerContentReady()) { ApplyDropTables(); } } public void AddItem(string dropTableId, DropTableItemSpawnModel item) { if (string.IsNullOrWhiteSpace(dropTableId)) { throw new ArgumentException("Drop table id cannot be empty.", "dropTableId"); } if (item == null) { throw new ArgumentNullException("item"); } lock (Sync) { DropTableItems.Add(new DropTableItemRegistration(dropTableId, item)); } if (IsServerContentReady()) { ApplyDropTables(); } } } private sealed class DropTableRegistration { public string Id { get; } public DropTableModel Model { get; } public DropTableRegistration(string id, DropTableModel model) { Id = id; Model = model; } } private sealed class DropTableItemRegistration { public string DropTableId { get; } public DropTableItemSpawnModel Item { get; } public DropTableItemRegistration(string dropTableId, DropTableItemSpawnModel item) { DropTableId = dropTableId; Item = item; } } public sealed class IconContentRegistry { public void RegisterPng(string id, string path) { Register(id, delegate(IconRegistrationOptions icon) { icon.UsePng(path); }); } public void RegisterSpriteSheetPng(string id, string path, int spriteSize, int frame = 0) { RegisterSpriteSheetPng(id, path, spriteSize, spriteSize, frame); } public void RegisterSpriteSheetPng(string id, string path, int spriteWidth, int spriteHeight, int frame = 0) { Register(id, delegate(IconRegistrationOptions icon) { icon.UseSpriteSheetPng(path, spriteWidth, spriteHeight, frame); }); } public void Register(string id, Action<IconRegistrationOptions> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Icon id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } IconRegistrationOptions iconRegistrationOptions = new IconRegistrationOptions(); configure(iconRegistrationOptions); lock (Sync) { IconRegistrations[id] = new IconRegistration(id, iconRegistrationOptions.Clone()); } if (IsClientContentReady()) { ApplyIcons(); } } } public sealed class IconRegistrationOptions { public IconVariantRegistration Small { get; } = new IconVariantRegistration(16, 16); public IconVariantRegistration Medium { get; } = new IconVariantRegistration(24, 24); public IconVariantRegistration Large { get; } = new IconVariantRegistration(32, 32); public IconVariantRegistration Huge { get; } = new IconVariantRegistration(96, 96); public void UsePng(string path) { Small.SetScaledFull(path, 16, 16); Medium.SetScaledFull(path, 24, 24); Large.SetScaledFull(path, 32, 32); Huge.SetScaledFull(path, 96, 96); } public void UseSpriteSheetPng(string path, int spriteSize, int frame = 0) { UseSpriteSheetPng(path, spriteSize, spriteSize, frame); } public void UseSpriteSheetPng(string path, int spriteWidth, int spriteHeight, int frame = 0) { Small.SetScaled(path, spriteWidth, spriteHeight, 16, 16, frame); Medium.SetScaled(path, spriteWidth, spriteHeight, 24, 24, frame); Large.SetScaled(path, spriteWidth, spriteHeight, 32, 32, frame); Huge.SetScaled(path, spriteWidth, spriteHeight, 96, 96, frame); } public void SetSmall(string path, int spriteWidth = 16, int spriteHeight = 16, int frame = 0) { Small.Set(path, spriteWidth, spriteHeight, frame); } public void SetMedium(string path, int spriteWidth = 24, int spriteHeight = 24, int frame = 0) { Medium.Set(path, spriteWidth, spriteHeight, frame); } public void SetLarge(string path, int spriteWidth = 32, int spriteHeight = 32, int frame = 0) { Large.Set(path, spriteWidth, spriteHeight, frame); } public void SetHuge(string path, int spriteWidth = 96, int spriteHeight = 96, int frame = 0) { Huge.Set(path, spriteWidth, spriteHeight, frame); } internal IconRegistrationOptions Clone() { IconRegistrationOptions iconRegistrationOptions = new IconRegistrationOptions(); iconRegistrationOptions.Small.CopyFrom(Small); iconRegistrationOptions.Medium.CopyFrom(Medium); iconRegistrationOptions.Large.CopyFrom(Large); iconRegistrationOptions.Huge.CopyFrom(Huge); return iconRegistrationOptions; } } public sealed class IconVariantRegistration { public string Path { get; set; } public int SpriteWidth { get; set; } public int SpriteHeight { get; set; } public int Frame { get; set; } internal int TargetWidth { get; private set; } internal int TargetHeight { get; private set; } internal bool ScaleToTarget { get; private set; } internal bool UseFullSource { get; private set; } internal IconVariantRegistration(int spriteWidth, int spriteHeight) { SpriteWidth = spriteWidth; SpriteHeight = spriteHeight; } internal void Set(string path, int spriteWidth, int spriteHeight, int frame) { SetCore(path, spriteWidth, spriteHeight, spriteWidth, spriteHeight, frame, scaleToTarget: false, useFullSource: false); } internal void SetScaled(string path, int spriteWidth, int spriteHeight, int targetWidth, int targetHeight, int frame) { SetCore(path, spriteWidth, spriteHeight, targetWidth, targetHeight, frame, scaleToTarget: true, useFullSource: false); } internal void SetScaledFull(string path, int targetWidth, int targetHeight) { SetCore(path, targetWidth, targetHeight, targetWidth, targetHeight, 0, scaleToTarget: true, useFullSource: true); } private void SetCore(string path, int spriteWidth, int spriteHeight, int targetWidth, int targetHeight, int frame, bool scaleToTarget, bool useFullSource) { if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException("Icon path cannot be empty.", "path"); } if (spriteWidth <= 0) { throw new ArgumentOutOfRangeException("spriteWidth", "Icon sprite width must be greater than zero."); } if (spriteHeight <= 0) { throw new ArgumentOutOfRangeException("spriteHeight", "Icon sprite height must be greater than zero."); } if (targetWidth <= 0) { throw new ArgumentOutOfRangeException("targetWidth", "Icon target width must be greater than zero."); } if (targetHeight <= 0) { throw new ArgumentOutOfRangeException("targetHeight", "Icon target height must be greater than zero."); } if (frame < 0) { throw new ArgumentOutOfRangeException("frame", "Icon frame cannot be negative."); } Path = path; SpriteWidth = spriteWidth; SpriteHeight = spriteHeight; Frame = frame; TargetWidth = targetWidth; TargetHeight = targetHeight; ScaleToTarget = scaleToTarget; UseFullSource = useFullSource; } internal void CopyFrom(IconVariantRegistration other) { Path = other.Path; SpriteWidth = other.SpriteWidth; SpriteHeight = other.SpriteHeight; Frame = other.Frame; TargetWidth = other.TargetWidth; TargetHeight = other.TargetHeight; ScaleToTarget = other.ScaleToTarget; UseFullSource = other.UseFullSource; } } private sealed class IconRegistration { public string Id { get; } public IconRegistrationOptions Options { get; } public IconRegistration(string id, IconRegistrationOptions options) { Id = id; Options = options; } } public sealed class ItemContentRegistry { public void Register(string id, string name, Action<ItemData> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Item id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } RegistrationSource registrationSource = GetRegistrationSource(); lock (Sync) { ItemRegistrations[id] = new ItemRegistration(id, name, null, configure, registrationSource); if (!string.IsNullOrEmpty(name)) { Strings[ItemNameKey(id)] = name; } } LogItemRegistration(registrationSource, id); ApplyLocalization(); if (IsSharedContentReady()) { ApplyItems(); } } public void Register(string id, string name, string description, Action<ItemData> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Item id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } RegistrationSource registrationSource = GetRegistrationSource(); lock (Sync) { ItemRegistrations[id] = new ItemRegistration(id, name, description, configure, registrationSource); if (!string.IsNullOrEmpty(name)) { Strings[ItemNameKey(id)] = name; } if (!string.IsNullOrEmpty(description)) { Strings[ItemDescriptionKey(id)] = description; } } LogItemRegistration(registrationSource, id); ApplyLocalization(); if (IsSharedContentReady()) { ApplyItems(); } } public void RegisterTombstone(string id, string name, string description = null, string icon = "sam", int maxStackSize = 99) { Register(id, name, description ?? "This item belongs to missing or removed mod content.", delegate(ItemData item) { item.Icon = (string.IsNullOrWhiteSpace(icon) ? "sam" : icon); item.MaxStackSize = Math.Max(1, maxStackSize); }); } } private sealed class ItemRegistration { public string Id { get; } public string Name { get; } public string Description { get; } public Action<ItemData> Configure { get; } public RegistrationSource Source { get; } public ItemRegistration(string id, string name, string description, Action<ItemData> configure) : this(id, name, description, configure, new RegistrationSource("Unknown mod", "unknown", "unknown")) { } public ItemRegistration(string id, string name, string description, Action<ItemData> configure, RegistrationSource source) { Id = id; Name = name; Description = description; Configure = configure; Source = source; } } public sealed class JobContentRegistry { public void Register(JobData job, string name, string feminineName = null) { if (job == null) { throw new ArgumentNullException("job"); } if (string.IsNullOrWhiteSpace(job.Id)) { throw new ArgumentException("Job id cannot be empty.", "job"); } if (job.MaxLevelUpgradeSteps == null || job.MaxLevelUpgradeSteps.Length == 0) { throw new ArgumentException("Job must define at least one level upgrade step.", "job"); } lock (Sync) { JobRegistrations[job.Id] = new JobRegistration(job, name, feminineName ?? name); if (!string.IsNullOrEmpty(name)) { Strings[JobNameKey(job.Id)] = name; } if (!string.IsNullOrEmpty(feminineName ?? name)) { Strings[JobFeminineNameKey(job.Id)] = feminineName ?? name; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyJobs(); } } } private sealed class JobRegistration { public JobData Job { get; } public string Name { get; } public string FeminineName { get; } public JobRegistration(JobData job, string name, string feminineName) { Job = job; Name = name; FeminineName = feminineName; } } public sealed class LocalizationContentRegistry { public void Register(string key, string text) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Localization key cannot be empty.", "key"); } if (text == null) { throw new ArgumentNullException("text"); } lock (Sync) { Strings[key] = text; } ApplyLocalization(); } } private readonly struct RegistrationSource { public string Name { get; } public string Guid { get; } public string AssemblyName { get; } public string DisplayName { get { if (!string.IsNullOrWhiteSpace(Guid)) { string.Equals(Name, Guid, StringComparison.Ordinal); } return Name; } } public RegistrationSource(string name, string guid, string assemblyName) { Name = (string.IsNullOrWhiteSpace(name) ? assemblyName : name); Guid = (string.IsNullOrWhiteSpace(guid) ? assemblyName : guid); AssemblyName = assemblyName; } } public sealed class MapContentRegistry { public void Register(string id, string cmxPath, string tmxPath) { Register(id, delegate(MapRegistrationOptions options) { options.CmxPath = cmxPath; options.TmxPath = tmxPath; }); } public void Register(string id, Action<MapRegistrationOptions> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Map id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } MapRegistrationOptions mapRegistrationOptions = new MapRegistrationOptions(); configure(mapRegistrationOptions); mapRegistrationOptions.Normalize(); string text = NormalizeMapId(id); ValidateMapRegistration(text, mapRegistrationOptions); lock (Sync) { MapRegistrations[text] = new MapRegistration(text, mapRegistrationOptions.Clone()); } ClearClientExteriorWorldCache(text); if (AreServerMapsReady()) { ApplyMaps(); } } } public sealed class MapRegistrationOptions { public string CmxPath { get; set; } public string TmxPath { get; set; } public string ContentRoot { get; set; } internal void Normalize() { CmxPath = NormalizePath(CmxPath); TmxPath = NormalizePath(TmxPath); ContentRoot = NormalizePath(ContentRoot); } internal MapRegistrationOptions Clone() { return new MapRegistrationOptions { CmxPath = CmxPath, TmxPath = TmxPath, ContentRoot = ContentRoot }; } } private sealed class MapRegistration { public string Id { get; } public string CmxPath { get; } public string TmxPath { get; } public string ContentRoot { get; } public MapRegistration(string id, MapRegistrationOptions options) { Id = id; CmxPath = options.CmxPath; TmxPath = options.TmxPath; ContentRoot = options.ContentRoot; } } public sealed class ItemRecipeContentRegistry { public void Register(ItemRecipe recipe, bool unlockedByDefault = true) { RegisterCore(recipe, unlockedByDefault, null); } public void Register(ItemRecipe recipe, Action<ItemRecipeRegistrationOptions> configure) { if (configure == null) { throw new ArgumentNullException("configure"); } ItemRecipeRegistrationOptions itemRecipeRegistrationOptions = new ItemRecipeRegistrationOptions(); configure(itemRecipeRegistrationOptions); RegisterCore(recipe, itemRecipeRegistrationOptions.UnlockedByDefault, itemRecipeRegistrationOptions.UnlockConditions); } public bool Unlock(string recipeId) { return SetUnlocked(recipeId, unlocked: true); } public int UnlockMany(params string[] recipeIds) { return SetUnlockedMany(recipeIds, unlocked: true); } public bool Lock(string recipeId) { return SetUnlocked(recipeId, unlocked: false); } public int LockMany(params string[] recipeIds) { return SetUnlockedMany(recipeIds, unlocked: false); } public bool IsUnlocked(string recipeId) { if (string.IsNullOrWhiteSpace(recipeId)) { return false; } lock (Sync) { ItemRecipeRegistration value; return ItemRecipes.TryGetValue(recipeId, out value) && IsRecipeUnlocked(value); } } public IReadOnlyList<ItemRecipe> GetUnlocked() { lock (Sync) { List<ItemRecipe> list = new List<ItemRecipe>(); foreach (ItemRecipeRegistration value in ItemRecipes.Values) { if (IsRecipeUnlocked(value)) { list.Add(value.Recipe); } } return list; } } public void RefreshUnlocks() { ApplyPlayerRecipeUnlocks(); } private static void RegisterCore(ItemRecipe recipe, bool unlockedByDefault, IReadOnlyList<Func<bool>> unlockConditions) { if (recipe == null) { throw new ArgumentNullException("recipe"); } if (string.IsNullOrWhiteSpace(recipe.Id)) { throw new ArgumentException("Recipe id cannot be empty.", "recipe"); } RegistrationSource registrationSource = GetRegistrationSource(); lock (Sync) { ItemRecipes[recipe.Id] = new ItemRecipeRegistration(recipe, unlockedByDefault, unlockConditions, registrationSource); } if (IsSharedContentReady()) { ApplyItemRecipes(); } } private static bool SetUnlocked(string recipeId, bool unlocked) { if (string.IsNullOrWhiteSpace(recipeId)) { throw new ArgumentException("Recipe id cannot be empty.", "recipeId"); } bool flag; lock (Sync) { if (!ItemRecipes.TryGetValue(recipeId, out var value)) { LogWarning($"Cannot {(unlocked ? "unlock" : "lock")} missing recipe '{recipeId}'."); return false; } flag = value.UnlockOverride != unlocked; value.UnlockOverride = unlocked; } if (flag) { ApplyPlayerRecipeUnlocks(); } return flag; } private static int SetUnlockedMany(string[] recipeIds, bool unlocked) { if (recipeIds == null) { throw new ArgumentNullException("recipeIds"); } int num = 0; for (int i = 0; i < recipeIds.Length; i++) { if (SetUnlocked(recipeIds[i], unlocked)) { num++; } } return num; } } public sealed class ItemRecipeRegistrationOptions { private readonly List<Func<bool>> unlockConditions = new List<Func<bool>>(); public bool UnlockedByDefault { get; set; } = true; internal IReadOnlyList<Func<bool>> UnlockConditions => unlockConditions; public void UnlockWhen(Func<bool> condition) { if (condition == null) { throw new ArgumentNullException("condition"); } unlockConditions.Add(condition); } public void UnlockWhenWorldFlag(string flagName) { if (string.IsNullOrWhiteSpace(flagName)) { throw new ArgumentException("World flag cannot be empty.", "flagName"); } UnlockWhen(() => GameState.WorldFlags != null && WorldFlagsHelper.HasFlag(GameState.WorldFlags, flagName)); } public void UnlockWhenWorldFlagIntAtLeast(string flagName, int value) { if (string.IsNullOrWhiteSpace(flagName)) { throw new ArgumentException("World flag cannot be empty.", "flagName"); } UnlockWhen(() => GameState.WorldFlags != null && WorldFlagsHelper.GetIntValue(GameState.WorldFlags, flagName, 0) >= value); } public void UnlockWhenWorldFlagIntEquals(string flagName, int value) { if (string.IsNullOrWhiteSpace(flagName)) { throw new ArgumentException("World flag cannot be empty.", "flagName"); } UnlockWhen(() => GameState.WorldFlags != null && WorldFlagsHelper.GetIntValue(GameState.WorldFlags, flagName, 0) == value); } public void UnlockWhenWorldFlagFloatAtLeast(string flagName, float value) { if (string.IsNullOrWhiteSpace(flagName)) { throw new ArgumentException("World flag cannot be empty.", "flagName"); } UnlockWhen(() => GameState.WorldFlags != null && WorldFlagsHelper.GetFloatValue(GameState.WorldFlags, flagName, 0f) >= value); } } private sealed class ItemRecipeRegistration { public ItemRecipe Recipe { get; } public bool UnlockedByDefault { get; } public bool? UnlockOverride { get; set; } public IReadOnlyList<Func<bool>> UnlockConditions { get; } public RegistrationSource Source { get; } public ItemRecipeRegistration(ItemRecipe recipe, bool unlocked, IReadOnlyList<Func<bool>> unlockConditions) : this(recipe, unlocked, unlockConditions, new RegistrationSource("Unknown mod", "unknown", "unknown")) { } public ItemRecipeRegistration(ItemRecipe recipe, bool unlocked, IReadOnlyList<Func<bool>> unlockConditions, RegistrationSource source) { Recipe = recipe; UnlockedByDefault = unlocked; IReadOnlyList<Func<bool>> readOnlyList2; if (unlockConditions != null) { IReadOnlyList<Func<bool>> readOnlyList = new List<Func<bool>>(unlockConditions); readOnlyList2 = readOnlyList; } else { IReadOnlyList<Func<bool>> readOnlyList = Array.Empty<Func<bool>>(); readOnlyList2 = readOnlyList; } UnlockConditions = readOnlyList2; Source = source; } } public sealed class ShopContentRegistry { public void Register(Shop shop) { if (shop == null) { throw new ArgumentNullException("shop"); } if (string.IsNullOrWhiteSpace(shop.Id)) { throw new ArgumentException("Shop id cannot be empty.", "shop"); } lock (Sync) { ShopRegistrations[shop.Id] = new ShopRegistration(shop); } if (IsSharedContentReady()) { ApplyShops(); } } public void AddItem(string shopId, string itemId) { if (string.IsNullOrWhiteSpace(shopId)) { throw new ArgumentException("Shop id cannot be empty.", "shopId"); } if (string.IsNullOrWhiteSpace(itemId)) { throw new ArgumentException("Item id cannot be empty.", "itemId"); } lock (Sync) { ShopItems.Add(new ShopItemRegistration(shopId, itemId)); } if (IsSharedContentReady()) { ApplyShops(); } } } private sealed class ShopRegistration { public Shop Shop { get; } public ShopRegistration(Shop shop) { Shop = shop; } } private sealed class ShopItemRegistration { public string ShopId { get; } public string ItemId { get; } public ShopItemRegistration(string shopId, string itemId) { ShopId = shopId; ItemId = itemId; } } public sealed class SpellContentRegistry { public void RegisterEffect(string id, string displayName, Action<CustomEffectRegistrationOptions> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Spell id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } CustomEffectRegistrationOptions customEffectRegistrationOptions = new CustomEffectRegistrationOptions(); configure(customEffectRegistrationOptions); lock (Sync) { SpellRegistrations[id] = new SpellRegistration(id, displayName, customEffectRegistrationOptions.Clone()); if (!string.IsNullOrEmpty(displayName)) { Strings[SpellNameKey(id)] = displayName; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplySpells(); } if (IsClientContentReady()) { ApplyClientSpells(); } if (IsServerContentReady()) { ApplyServerSpells(); } } } public sealed class CustomEffectRegistrationOptions { public Func<CustomClientEffectContext, CanCastSpellResult> CanCastClient { get; set; } public Action<CustomClientEffectContext> OnClientCast { get; set; } public Func<CustomServerEffectContext, CanCastSpellResult> CanCastServer { get; set; } public Action<CustomServerEffectContext> OnServerCast { get; set; } public string CastSoundEffect { get; set; } public float SpellDuration { get; set; } internal CustomEffectRegistrationOptions Clone() { return new CustomEffectRegistrationOptions { CanCastClient = CanCastClient, OnClientCast = OnClientCast, CanCastServer = CanCastServer, OnServerCast = OnServerCast, CastSoundEffect = CastSoundEffect, SpellDuration = SpellDuration }; } } public readonly struct CustomClientEffectContext { public SpellCastingContext Context { get; } public CastSpellArgs CastSpellArgs { get; } internal CustomClientEffectContext(SpellCastingContext context, CastSpellArgs castSpellArgs) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Context = context; CastSpellArgs = castSpellArgs; } } public readonly struct CustomServerEffectContext { public ServerSpellCastingContext Context { get; } public CastSpellArgs CastSpellArgs { get; } internal CustomServerEffectContext(ServerSpellCastingContext context, CastSpellArgs castSpellArgs) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Context = context; CastSpellArgs = castSpellArgs; } } private sealed class CustomClientEffectSpell : AbstractSpell { public override CanCastSpellResult CanCast(SpellCastingContext context) { //IL_0000: 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_0062: 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) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) SpellRegistration spellRegistration = GetSpellRegistration(context.SpellTypeArgs); if (spellRegistration == null) { LogWarning("Cannot cast missing custom client effect."); return (CanCastSpellResult)1; } Func<CustomClientEffectContext, CanCastSpellResult> canCastClient = spellRegistration.Options.CanCastClient; if (canCastClient == null) { return (CanCastSpellResult)0; } try { return canCastClient(new CustomClientEffectContext(context, null)); } catch (Exception ex) { LogWarning("Custom client effect '" + spellRegistration.Id + "' CanCast failed: " + ex.Message); return (CanCastSpellResult)2; } } protected override void Cast(ClientProjectileSystem projectileSystem, SpellCastingContext context, CastSpellArgs castSpellArgs) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) SpellRegistration spellRegistration = GetSpellRegistration(context.SpellTypeArgs); if (spellRegistration == null) { return; } Action<CustomClientEffectContext> onClientCast = spellRegistration.Options.OnClientCast; if (onClientCast == null) { return; } try { onClientCast(new CustomClientEffectContext(context, castSpellArgs)); } catch (Exception ex) { LogWarning("Custom client effect '" + spellRegistration.Id + "' failed: " + ex.Message); } } } private sealed class CustomServerEffectSpell : AbstractServerSpell { public override CanCastSpellResult CanCast(ServerSpellCastingContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) SpellRegistration spellRegistration = GetSpellRegistration(context.Context.SpellTypeArgs); if (spellRegistration == null) { LogWarning("Cannot cast missing custom server effect."); return (CanCastSpellResult)1; } Func<CustomServerEffectContext, CanCastSpellResult> canCastServer = spellRegistration.Options.CanCastServer; if (canCastServer == null) { return (CanCastSpellResult)0; } try { return canCastServer(new CustomServerEffectContext(context, null)); } catch (Exception ex) { LogWarning("Custom server effect '" + spellRegistration.Id + "' CanCast failed: " + ex.Message); return (CanCastSpellResult)2; } } protected override void DoCast(ProjectileSystem projectileSystem, ServerSpellCastingContext context, CastSpellArgs castSpellArgs) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) SpellRegistration spellRegistration = GetSpellRegistration(context.Context.SpellTypeArgs); if (spellRegistration == null) { return; } Action<CustomServerEffectContext> onServerCast = spellRegistration.Options.OnServerCast; if (onServerCast == null) { return; } try { onServerCast(new CustomServerEffectContext(context, castSpellArgs)); } catch (Exception ex) { LogWarning("Custom server effect '" + spellRegistration.Id + "' failed: " + ex.Message); } } } private sealed class SpellRegistration { public string Id { get; } public string DisplayName { get; } public CustomEffectRegistrationOptions Options { get; } public SpellRegistration(string id, string displayName, CustomEffectRegistrationOptions options) { Id = id; DisplayName = displayName; Options = options; } } public sealed class VfxContentRegistry { public void RegisterSpriteSheetPng(string id, string path, int spriteSize, int frameCount, int frameDuration = 2) { RegisterSpriteSheetPng(id, path, spriteSize, spriteSize, frameCount, frameDuration); } public void RegisterSpriteSheetPng(string id, string path, int spriteWidth, int spriteHeight, int frameCount, int frameDuration = 2) { Register(id, delegate(VfxRegistrationOptions vfx) { vfx.UseSpriteSheetPng(path, spriteWidth, spriteHeight, frameCount, frameDuration); }); } public void Register(string id, Action<VfxRegistrationOptions> configure) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("VFX id cannot be empty.", "id"); } if (configure == null) { throw new ArgumentNullException("configure"); } VfxRegistrationOptions vfxRegistrationOptions = new VfxRegistrationOptions(); configure(vfxRegistrationOptions); lock (Sync) { VfxRegistrations[id] = new VfxRegistration(id, vfxRegistrationOptions.Clone()); } if (IsClientContentReady()) { ApplyVfx(); } } public void Play(string id, Vector3 position, float angle = 0f, float scale = 1f, SpriteEffects spriteEffects = (SpriteEffects)0) { //IL_0012: 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) VfxSpritesAnimationModel animation = GetAnimation(id); if (!(animation == (VfxSpritesAnimationModel)null)) { VfxPlayer.PlayAnimation(animation, position, angle, scale, spriteEffects); } } public void Play(string id, EntityWrapper target, float angle = 0f, float scale = 1f, SpriteEffects spriteEffects = (SpriteEffects)0) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) VfxSpritesAnimationModel animation = GetAnimation(id); if (!(animation == (VfxSpritesAnimationModel)null)) { VfxPlayer.PlayAnimation(animation, target, angle, scale, spriteEffects); } } private static VfxSpritesAnimationModel GetAnimation(string id) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("VFX id cannot be empty.", "id"); } VfxSpritesAnimationModel vfxSpritesAnimationOrNull = VfxSpritesAnimationDataBase.GetVfxSpritesAnimationOrNull(id); if (vfxSpritesAnimationOrNull == (VfxSpritesAnimationModel)null && IsClientContentReady()) { ApplyVfx(); vfxSpritesAnimationOrNull = VfxSpritesAnimationDataBase.GetVfxSpritesAnimationOrNull(id); } if (vfxSpritesAnimationOrNull == (VfxSpritesAnimationModel)null) { LogWarning("Cannot play missing VFX animation '" + id + "'."); } return vfxSpritesAnimationOrNull; } } public sealed class VfxRegistrationOptions { public string Path { get; set; } public int SpriteWidth { get; set; } = 32; public int SpriteHeight { get; set; } = 32; public int FirstFrame { get; set; } public int FrameCount { get; set; } = 1; public int FrameDuration { get; set; } = 2; public float Scale { get; set; } = 1f; public float Opacity { get; set; } = 1f; public Vector3 Offset { get; set; } = Vector3.Zero; public bool Is3D { get; set; } public bool IsHorizontalPlane { get; set; } public void UseSpriteSheetPng(string path, int spriteSize, int frameCount, int frameDuration = 2) { UseSpriteSheetPng(path, spriteSize, spriteSize, frameCount, frameDuration); } public void UseSpriteSheetPng(string path, int spriteWidth, int spriteHeight, int frameCount, int frameDuration = 2) { if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException("VFX path cannot be empty.", "path"); } if (spriteWidth <= 0) { throw new ArgumentOutOfRangeException("spriteWidth", "VFX sprite width must be greater than zero."); } if (spriteHeight <= 0) { throw new ArgumentOutOfRangeException("spriteHeight", "VFX sprite height must be greater than zero."); } if (frameCount <= 0) { throw new ArgumentOutOfRangeException("frameCount", "VFX frame count must be greater than zero."); } if (frameDuration <= 0) { throw new ArgumentOutOfRangeException("frameDuration", "VFX frame duration must be greater than zero."); } Path = path; SpriteWidth = spriteWidth; SpriteHeight = spriteHeight; FrameCount = frameCount; FrameDuration = frameDuration; } internal VfxRegistrationOptions Clone() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) return new VfxRegistrationOptions { Path = Path, SpriteWidth = SpriteWidth, SpriteHeight = SpriteHeight, FirstFrame = FirstFrame, FrameCount = FrameCount, FrameDuration = FrameDuration, Scale = Scale, Opacity = Opacity, Offset = Offset, Is3D = Is3D, IsHorizontalPlane = IsHorizontalPlane }; } } private sealed class VfxRegistration { public string Id { get; } public VfxRegistrationOptions Options { get; } public VfxRegistration(string id, VfxRegistrationOptions options) { Id = id; Options = options; } } public sealed class WorkRecipeContentRegistry { public void Register(WorkRecipeModel recipe, string name = null, string description = null) { WorkRecipeRegistrationOptions options = new WorkRecipeRegistrationOptions { Name = name, Description = description }; RegisterCore(recipe, options); } public void Register(WorkRecipeModel recipe, Action<WorkRecipeRegistrationOptions> configure) { if (configure == null) { throw new ArgumentNullException("configure"); } WorkRecipeRegistrationOptions workRecipeRegistrationOptions = new WorkRecipeRegistrationOptions(); configure(workRecipeRegistrationOptions); RegisterCore(recipe, workRecipeRegistrationOptions); } private static void RegisterCore(WorkRecipeModel recipe, WorkRecipeRegistrationOptions options) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) if (recipe == null) { throw new ArgumentNullException("recipe"); } if (string.IsNullOrWhiteSpace(recipe.Id)) { throw new ArgumentException("Work recipe id cannot be empty.", "recipe"); } if (options.LevelRequirement.HasValue) { if (options.LevelRequirement.Value < 0) { throw new ArgumentOutOfRangeException("options", "Work recipe level requirement cannot be negative."); } recipe.LevelRequirement = options.LevelRequirement.Value; } lock (Sync) { WorkRecipeRegistrations[recipe.Id] = new WorkRecipeRegistration(recipe, options); if (!string.IsNullOrEmpty(options.Name)) { recipe.Name = StringId.op_Implicit(recipe.Id + "*work_recipe:name"); Strings[recipe.Id + "*work_recipe:name"] = options.Name; } if (!string.IsNullOrEmpty(options.Description)) { recipe.Description = StringId.op_Implicit(recipe.Id + "*work_recipe:description"); Strings[recipe.Id + "*work_recipe:description"] = options.Description; } } ApplyLocalization(); if (IsSharedContentReady()) { ApplyWorkRecipes(); } if (AreWorkRecipeGroupsReady()) { ApplyWorkRecipeGroups(); } if (IsServerContentReady()) { ApplyWorkRecipeUnlocks(); } } } public sealed class WorkRecipeRegistrationOptions { private readonly List<string> unlockJobIds = new List<string>(); private readonly List<WorkRecipeJobLevelGroup> jobLevelGroups = new List<WorkRecipeJobLevelGroup>(); public string Name { get; set; } public string Description { get; set; } public int? LevelRequirement { get; set; } internal IReadOnlyList<string> UnlockJobIds => unlockJobIds; internal IReadOnlyList<WorkRecipeJobLevelGroup> JobLevelGroups => jobLevelGroups; public void AddToJobGroup(string jobId, int groupLevel, WorkRecipePlacement placement = null) { if (string.IsNullOrWhiteSpace(jobId)) { throw new ArgumentException("Job id cannot be empty.", "jobId"); } if (groupLevel <= 0) { throw new ArgumentOutOfRangeException("groupLevel", "Work recipe group level must be greater than zero."); } WorkRecipeJobLevelGroup item = new WorkRecipeJobLevelGroup(jobId, groupLevel, placement ?? WorkRecipePlacement.End); if (!jobLevelGroups.Contains(item)) { jobLevelGroups.Add(item); } } public void UnlockForJob(string jobId) { if (string.IsNullOrWhiteSpace(jobId)) { throw new ArgumentException("Job id cannot be empty.", "jobId"); } if (!unlockJobIds.Contains(jobId)) { unlockJobIds.Add(jobId); } } } public sealed class WorkRecipePlacement { public static WorkRecipePlacement End { get; } = new WorkRecipePlacement(WorkRecipePlacementKind.End, null); public static WorkRecipePlacement Start { get; } = new WorkRecipePlacement(WorkRecipePlacementKind.Start, null); internal WorkRecipePlacementKind Kind { get; } internal string AnchorRecipeId { get; } private WorkRecipePlacement(WorkRecipePlacementKind kind, string anchorRecipeId) { Kind = kind; AnchorRecipeId = anchorRecipeId; } public static WorkRecipePlacement Before(string recipeId) { if (string.IsNullOrWhiteSpace(recipeId)) { throw new ArgumentException("Anchor recipe id cannot be empty.", "recipeId"); } return new WorkRecipePlacement(WorkRecipePlacementKind.Before, recipeId); } public static WorkRecipePlacement After(string recipeId) { if (string.IsNullOrWhiteSpace(recipeId)) { throw new ArgumentException("Anchor recipe id cannot be empty.", "recipeId"); } return new WorkRecipePlacement(WorkRecipePlacementKind.After, recipeId); } public override bool Equals(object obj) { if (obj is WorkRecipePlacement workRecipePlacement && Kind == workRecipePlacement.Kind) { return string.Equals(AnchorRecipeId, workRecipePlacement.AnchorRecipeId, StringComparison.Ordinal); } return false; } public override int GetHashCode() { return ((int)Kind * 397) ^ ((AnchorRecipeId != null) ? StringComparer.Ordinal.GetHashCode(AnchorRecipeId) : 0); } } internal enum WorkRecipePlacementKind { End, Start, Before, After } internal readonly struct WorkRecipeJobLevelGroup : IEquatable<WorkRecipeJobLevelGroup> { public string JobId { get; } public int Level { get; } public WorkRecipePlacement Placement { get; } public WorkRecipeJobLevelGroup(string jobId, int level, WorkRecipePlacement placement) { JobId = jobId; Level = level; Placement = placement; } public string GetGroupId() { return $"{JobId}:{Level}"; } public bool Equals(WorkRecipeJobLevelGroup other) { if (string.Equals(JobId, other.JobId, StringComparison.Ordinal) && Level == other.Level) { return Placement.Equals(other.Placement); } return false; } public override bool Equals(object obj) { if (obj is WorkRecipeJobLevelGroup other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((JobId != null) ? StringComparer.Ordinal.GetHashCode(JobId) : 0) * 397) ^ Level) * 397) ^ Placement.GetHashCode(); } } private readonly struct WorkRecipeJobUnlock { public string RecipeId { get; } public string JobId { get; } public WorkRecipeJobUnlock(string recipeId, string jobId) { RecipeId = recipeId; JobId = jobId; } } private sealed class WorkRecipeRegistration { public WorkRecipeModel Recipe { get; } public string Name { get; } public string Description { get; } public IReadOnlyList<string> UnlockJobIds { get; } public IReadOnlyList<WorkRecipeJobLevelGroup> JobGroups { get; } public WorkRecipeRegistration(WorkRecipeModel recipe, WorkRecipeRegistrationOptions options) { Recipe = recipe; Name = options.Name; Description = options.Description; UnlockJobIds = new List<string>(options.UnlockJobIds); JobGroups = new List<WorkRecipeJobLevelGroup>(options.JobLevelGroups); } } private const string StandardCreatedDayStateKey = "romestead_content_framework:standard:created_day"; private const string StandardLastDayStateKey = "romestead_content_framework:standard:last_day"; private const string StandardUserStatePrefix = "romestead_content_framework:state:"; private const int StandardStorageInventorySize = 24; private const int StandardOutputInventorySize = 18; private const string StandardStorageInventoryNameKey = "Object_Storage"; private const string StandardOutputInventoryNameKey = "Buildings_Output"; private static readonly HashSet<string> StandardBehaviorWarnings = new HashSet<string>(StringComparer.Ordinal); internal const string CustomEffectSpellTypeId = "romestead_content_framework:spelltype:custom_effect"; private static readonly object Sync = new object(); private static readonly Dictionary<string, ItemRegistration> ItemRegistrations = new Dictionary<string, ItemRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, string> Strings = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly Dictionary<string, SpellRegistration> SpellRegistrations = new Dictionary<string, SpellRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, BuildingTypeRegistration> BuildingTypeRegistrations = new Dictionary<string, BuildingTypeRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, StandardBuildingBehaviorRegistration> StandardBuildingBehaviors = new Dictionary<string, StandardBuildingBehaviorRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, BuildingRegistration> BuildingRegistrations = new Dictionary<string, BuildingRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, ConstructionRegistration> ConstructionRegistrations = new Dictionary<string, ConstructionRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, ConstructionGroupRegistration> ConstructionGroupRegistrations = new Dictionary<string, ConstructionGroupRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, ConstructionResourceRegistration> ConstructionResourceRegistrations = new Dictionary<string, ConstructionResourceRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, CraftingStationRegistration> CraftingStationRegistrations = new Dictionary<string, CraftingStationRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, DecorationRegistration> DecorationRegistrations = new Dictionary<string, DecorationRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, FurnitureRegistration> FurnitureRegistrations = new Dictionary<string, FurnitureRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, MapRegistration> MapRegistrations = new Dictionary<string, MapRegistration>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> DefaultUnlockedConstructionIds = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<string, ItemRecipeRegistration> ItemRecipes = new Dictionary<string, ItemRecipeRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, WorkRecipeRegistration> WorkRecipeRegistrations = new Dictionary<string, WorkRecipeRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, JobRegistration> JobRegistrations = new Dictionary<string, JobRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, ShopRegistration> ShopRegistrations = new Dictionary<string, ShopRegistration>(StringComparer.Ordinal); private static readonly List<ShopItemRegistration> ShopItems = new List<ShopItemRegistration>(); private static readonly Dictionary<string, DropTableRegistration> DropTables = new Dictionary<string, DropTableRegistration>(StringComparer.Ordinal); private static readonly List<DropTableItemRegistration> DropTableItems = new List<DropTableItemRegistration>(); private static readonly Dictionary<string, IconRegistration> IconRegistrations = new Dictionary<string, IconRegistration>(StringComparer.Ordinal); private static readonly Dictionary<string, VfxRegistration> VfxRegistrations = new Dictionary<string, VfxRegistration>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedItemIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedSpellIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedBuildingTypeIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedBuildingIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedConstructionIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedConstructionGroupIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedConstructionResourceIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedCraftingStationIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedDecorationIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedFurnitureIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedMapIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> AppliedItemRecipeIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedWorkRecipeIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedJobIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedShopIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedDropTableIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedIconIds = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> AppliedVfxIds = new HashSet<string>(StringComparer.Ordinal); private static bool SharedContentReady; private static bool WorkRecipeGroupsReady; private static bool ServerContentReady; private static bool ClientContentReady; private static bool ServerMapsReady; private static readonly Assembly FrameworkAssembly = typeof(RomesteadContent).Assembly; private const string MissingExteriorMapFallback = "maps/building_exteriors/leatherworker_1"; private const string MissingInteriorMapFallback = "maps/interiors_new/leather_1"; private static readonly HashSet<string> MissingMapFallbackWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, string> ClientCustomDoodadDatabaseSources = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, HashSet<Guid>> ClientCustomDoodadDatabaseGuids = new Dictionary<string, HashSet<Guid>>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, string> ServerCustomDoodadDatabaseSources = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, HashSet<Guid>> ServerCustomDoodadDatabaseGuids = new Dictionary<string, HashSet<Guid>>(StringComparer.OrdinalIgnoreCase); public static ItemContentRegistry Items { get; } = new ItemContentRegistry(); public static LocalizationContentRegistry Localization { get; } = new LocalizationContentRegistry(); public static SpellContentRegistry Spells { get; } = new SpellContentRegistry(); public static BuildingContentRegistry Buildings { get; } = new BuildingContentRegistry(); public static MapContentRegistry Maps { get; } = new MapContentRegistry(); public static ItemRecipeContentRegistry Recipes { get; } = new ItemRecipeContentRegistry(); public static WorkRecipeContentRegistry WorkRecipes { get; } = new WorkRecipeContentRegistry(); public static JobContentRegistry Jobs { get; } = new JobContentRegistry(); public static ShopContentRegistry Shops { get; } = new ShopContentRegistry(); public static DropTableContentRegistry Drops { get; } = new DropTableContentRegistry(); public static IconContentRegistry Icons { get; } = new IconContentRegistry(); public static VfxContentRegistry Vfx { get; } = new VfxContentRegistry(); internal static void StandardBuildingCreated(StandardBuildingController controller) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior != null) { EnsureStandardInventories(((AbstractBuildingController)controller).Building, standardBuildingBehavior); SetInternalDay(((AbstractBuildingController)controller).Building.InstanceModel, "romestead_content_framework:standard:created_day", TimeServerSystem.Day, overwrite: false); InvokeStandardCallback(standardBuildingBehavior, "OnCreated", standardBuildingBehavior.Options.OnCreated, ((AbstractBuildingController)controller).Building); } } internal static void StandardBuildingLoaded(StandardBuildingController controller) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior != null) { ((AbstractBuildingController)controller).JobTickRate = standardBuildingBehavior.Options.WorkerJobTickRate; InvokeStandardCallback(standardBuildingBehavior, "OnLoaded", standardBuildingBehavior.Options.OnLoaded, ((AbstractBuildingController)controller).Building); } } internal static void StandardBuildingRemoved(StandardBuildingController controller) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior != null) { InvokeStandardCallback(standardBuildingBehavior, "OnRemoved", standardBuildingBehavior.Options.OnRemoved, ((AbstractBuildingController)controller).Building); } } internal static void StandardBuildingUpgraded(StandardBuildingController controller, ConstructionModel upgradeConstruction, BuildingData upgradedBuildingData) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior == null || standardBuildingBehavior.Options.OnUpgraded == null) { return; } try { standardBuildingBehavior.Options.OnUpgraded(new StandardBuildingUpgradeContext(((AbstractBuildingController)controller).Building, upgradeConstruction, upgradedBuildingData)); } catch (Exception ex) { LogWarning(standardBuildingBehavior.Source, "Standard building '" + standardBuildingBehavior.Id + "' OnUpgraded callback failed: " + ex.Message); } } internal static void HandleStandardBuildingNewDay() { List<BuildingSimulationModel> list = new List<BuildingSimulationModel>(ServerGameState.Buildings.Values); for (int i = 0; i < list.Count; i++) { if (list[i].Controller is StandardBuildingController controller) { HandleStandardBuildingNewDay(controller); } } } internal static bool TryGetStandardWorkerSettings(StandardBuildingController controller, out float jobTickRate, out float qualityModifier) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior == null || standardBuildingBehavior.Options.ProductionMode != StandardBuildingProductionMode.WorkerJob) { jobTickRate = 0.5f; qualityModifier = -0.5f; return false; } jobTickRate = standardBuildingBehavior.Options.WorkerJobTickRate; qualityModifier = standardBuildingBehavior.Options.WorkerQualityModifier; return true; } internal static void WarnUnsupportedStandardWorkOrder(StandardBuildingController controller, string outputType) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior != null) { LogWarning(standardBuildingBehavior.Source, $"Standard worker building '{standardBuildingBehavior.Id}' cannot finish work-order output type '{outputType}'."); } } private static void HandleStandardBuildingNewDay(StandardBuildingController controller) { StandardBuildingBehaviorRegistration standardBuildingBehavior = GetStandardBuildingBehavior(controller); if (standardBuildingBehavior == null) { return; } BuildingInstanceModel instanceModel = ((AbstractBuildingController)controller).Building.InstanceModel; int day = TimeServerSystem.Day; if (GetInternalDay(instanceModel, "romestead_content_framework:standard:last_day", int.MinValue) == day) { return; } SetInternalDay(instanceModel, "romestead_content_framework:standard:last_day", day, overwrite: true); int internalDay = GetInternalDay(instanceModel, "romestead_content_framework:standard:created_day", day); IReadOnlyList<StandardItemProduction> productions = standardBuildingBehavior.Options.Productions; int num = 0; while (standardBuildingBehavior.Options.ProductionMode == StandardBuildingProductionMode.AutomaticDaily && num < productions.Count) { StandardItemProduction standardItemProduction = productions[num]; int num2 = day - internalDay; if (num2 >= standardItemProduction.FirstDayDelay && (num2 - standardItemProduction.FirstDayDelay) % standardItemProduction.EveryDays == 0) { if (ItemDataBase.GetItemDataOrNull(standardItemProduction.ItemId) == null) { WarnStandardOnce(standardBuildingBehavior.Id + ":missing-item:" + standardItemProduction.ItemId, $"Standard building '{standardBuildingBehavior.Id}' cannot produce missing item '{standardItemProduction.ItemId}'. Source: {standardBuildingBehavior.Source.DisplayName}."); } else { new StandardBuildingContext(((AbstractBuildingController)controller).Building).TryAddOutputItem(standardItemProduction.ItemId, standardItemProduction.Amount); } } num++; } InvokeStandardCallback(standardBuildingBehavior, "OnNewDay", standardBuildingBehavior.Options.OnNewDay, ((AbstractBuildingController)controller).Building); } private static void ApplyStandardBuildingDefaults(BuildingData building) { StandardBuildingBehaviorRegistration value; lock (Sync) { if (!StandardBuildingBehaviors.TryGetValue(building.BuildingTypeId, out value)) { return; } } if (!building.Input.HasValue && value.Options.InputLogistics.HasValue) { building.Input = value.Options.InputLogistics; } if (!building.Output.HasValue && value.Options.OutputLogistics.HasValue) { building.Output = value.Options.OutputLogistics; } } private static StandardBuildingBehaviorRegistration GetStandardBuildingBehavior(StandardBuildingController controller) { StandardBuildingBehaviorRegistration value; lock (Sync) { StandardBuildingBehaviors.TryGetValue(((AbstractBuildingController)controller).Building.InstanceModel.BuildingTypeId, out value); } if (value == null) { WarnStandardOnce(((AbstractBuildingController)controller).Building.InstanceModel.BuildingTypeId + ":missing-registration", "Cannot run missing standard building behavior '" + ((AbstractBuildingController)controller).Building.InstanceModel.BuildingTypeId + "'."); } return value; } private static void EnsureStandardInventories(BuildingSimulationModel building, StandardBuildingBehaviorRegistration registration) { //IL_0027: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown StandardBuildingBehaviorOptions options = registration.Options; try { if (options.HasStorageInventory && !building.InstanceModel.StorageInventoryId.HasValue) { BuildingsSController.CreateBuildingInventory(building.InstanceModel, new CreateNewInventoryMessage { InventorySize = 24, InventoryType = (InventoryType)0, NewId = Guid.NewGuid(), NameId = StringId.op_Implicit("Object_Storage"), FilteredItemFlags = options.StorageItemFlags }, (IOInventoryCreationType)0); } if (options.HasOutputInventory && !building.InstanceModel.OutputInventoryId.HasValue) { BuildingsSController.CreateBuildingInventory(building.InstanceModel, new CreateNewInventoryMessage { InventorySize = 18, InventoryType = (InventoryType)2, NewId = Guid.NewGuid(), NameId = StringId.op_Implicit("Buildings_Output"), FilteredItemFlags = options.OutputItemFlags }, (IOInventoryCreationType)1); } } catch (Exception ex) { LogWarning(registration.Source, "Cannot create inventories for standard building '" + registration.Id + "': " + ex.Message); } } private static void InvokeStandardCallback(StandardBuildingBehaviorRegistration registration, string callbackName, Action<StandardBuildingContext> callback, BuildingSimulationModel building) { if (callback == null) { return; } try { callback(new StandardBuildingContext(building)); } catch (Exception ex) { LogWarning(registration.Source, $"Standard building '{registration.Id}' {callbackName} callback failed: {ex.Message}"); } } private static Dictionary<string, string> EnsureParameters(BuildingInstanceModel instance) { if (instance.Parameters == null) { instance.Parameters = new Dictionary<string, string>(); } return instance.Parameters; } private static int GetInternalDay(BuildingInstanceModel instance, string key, int fallback) { if (instance.Parameters == null || !instance.Parameters.TryGetValue(key, out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return fallback; } return result; } private static void SetInternalDay(BuildingInstanceModel instance, string key, int day, bool overwrite) { Dictionary<string, string> dictionary = EnsureParameters(instance); if (overwrite || !dictionary.ContainsKey(key)) { dictionary[key] = day.ToString(CultureInfo.InvariantCulture); } } private static void WarnStandardOnce(string key, string message) { lock (Sync) { if (!StandardBehaviorWarnings.Add(key)) { return; } } LogWarning(message); } private static void ApplyBuildingCatalogLocked() { ApplyConstructionGroupsLocked(); ApplyConstructionResourcesLocked(); ApplyCraftingStationsLocked(); ApplyDecorationsLocked(); ApplyFurnitureLocked(); } private static void ApplyConstructionGroupsLocked() { foreach (ConstructionGroupRegistration value in ConstructionGroupRegistrations.Values) { string id = value.Group.Id; if (PrepareDatabaseId(id, AppliedConstructionGroupIds, ConstructionsGroupDataBase.ConstructionsGroupDataMap, "construction group")) { ConstructionsGroupDataBase.AddData(new List<ConstructionsGroupData> { value.Group }); AppliedConstructionGroupIds.Add(id); } } } private static void ApplyConstructionResourcesLocked() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) foreach (ConstructionResourceRegistration value in ConstructionResourceRegistrations.Values) { string id = value.Resource.Id; if (PrepareDatabaseId(id, AppliedConstructionResourceIds, ConstructionResourcesDataBase.DataMap, "construction resource")) { ConstructionResourcesDataBase.AddConstructionMaterials(new List<ConstructionResourceDataModel> { value.Resource }); AppliedConstructionResourceIds.Add(id); } } } private static void ApplyCraftingStationsLocked() { foreach (CraftingStationRegistration value in CraftingStationRegistrations.Values) { string id = value.Station.Id; if (PrepareDatabaseId(id, AppliedCraftingStationIds, CraftingStationDataBase.DataMap, "crafting station")) { CraftingStationDataBase.AddCraftingStations(new List<CraftingStationData> { value.Station }); AppliedCraftingStationIds.Add(id); } } } private static void ApplyDecorationsLocked() { foreach (DecorationRegistration value in DecorationRegistrations.Values) { string id = value.Decoration.Id; if (PrepareDatabaseId(id, AppliedDecorationIds, DecorationsDataBase.DecorationDataMap, "decoration")) { DecorationsDataBase.AddData(new List<DecorationData> { value.Decoration }); AppliedDecorationIds.Add(id); } } } private static void ApplyFurnitureLocked() { foreach (FurnitureRegistration value2 in FurnitureRegistrations.Values) { string id = value2.Furniture.Id; if (FurnitureDataBase.DataMap.TryGetValue(id, out var value)) { if (!AppliedFurnitureIds.Contains(id) || value !=