/* * Copyright 2022 Peter Han * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using HarmonyLib; using PeterHan.PLib.Core; using PeterHan.PLib.PatchManager; using System; using System.Collections.Generic; using System.Reflection; namespace PeterHan.PLib.Buildings { /// /// Manages PLib buildings to break down PBuilding into a more reasonable sized class. /// public sealed class PBuildingManager : PForwardedComponent { /// /// The version of this component. Uses the running PLib version. /// internal static readonly Version VERSION = new Version(PVersion.VERSION); /// /// The instantiated copy of this class. /// internal static PBuildingManager Instance { get; private set; } /// /// Immediately adds an existing building ID to an existing technology ID in the /// tech tree. /// /// Do not use this method on buildings registered through PBuilding as they /// are added automatically. /// /// This method must be used in a Db.Initialize postfix patch or RunAt.AfterDbInit /// PPatchManager method/patch. /// /// The technology tree node ID. /// The building ID to add to that node. public static void AddExistingBuildingToTech(string tech, string id) { if (string.IsNullOrEmpty(tech)) throw new ArgumentNullException(nameof(tech)); if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id)); var technology = Db.Get().Techs?.TryGet(id); if (technology != null) technology.unlockedItemIDs?.Add(tech); } private static void CreateBuildingDef_Postfix(BuildingDef __result, string anim, string id) { var animFiles = __result?.AnimFiles; if (animFiles != null && animFiles.Length > 0 && animFiles[0] == null) Debug.LogWarningFormat("(when looking for KAnim named {0} on building {1})", anim, id); } private static void CreateEquipmentDef_Postfix(EquipmentDef __result, string Anim, string Id) { var anim = __result?.Anim; if (anim == null) Debug.LogWarningFormat("(when looking for KAnim named {0} on equipment {1})", Anim, Id); } /// /// Logs a message encountered by the PLib building system. /// /// The debug message. internal static void LogBuildingDebug(string message) { Debug.LogFormat("[PLibBuildings] {0}", message); } private static void LoadGeneratedBuildings_Prefix() { Instance?.AddAllStrings(); } /// /// The buildings which need to be registered. /// private readonly ICollection buildings; public override Version Version => VERSION; /// /// Creates a building manager to register PLib buildings. /// public PBuildingManager() { buildings = new List(16); } /// /// Adds the strings for every registered building in all mods to the database. /// private void AddAllStrings() { InvokeAllProcess(0, null); } /// /// Adds the strings for each registered building in this mod to the database. /// private void AddStrings() { int n = buildings.Count; if (n > 0) { LogBuildingDebug("Register strings for {0:D} building(s) from {1}".F(n, Assembly.GetExecutingAssembly().GetNameSafe() ?? "?")); foreach (var building in buildings) if (building != null) { building.AddStrings(); building.AddPlan(); } } } /// /// Adds the techs for every registered building in all mods to the database. /// private void AddAllTechs() { InvokeAllProcess(1, null); } /// /// Adds the techs for each registered building in this mod to the database. /// private void AddTechs() { int n = buildings.Count; if (n > 0) { LogBuildingDebug("Register techs for {0:D} building(s) from {1}".F(n, Assembly.GetExecutingAssembly().GetNameSafe() ?? "?")); foreach (var building in buildings) if (building != null) { building.AddTech(); } } } /// /// Registers a building to properly display its name, description, and tech tree /// entry. PLib must be initialized using InitLibrary before using this method. Each /// building should only be registered once, either in OnLoad or a post-load patch. /// /// The building to register. public void Register(PBuilding building) { if (building == null) throw new ArgumentNullException(nameof(building)); RegisterForForwarding(); // Must use object as the building table type buildings.Add(building); #if DEBUG PUtil.LogDebug("Registered building: {0}".F(building.ID)); #endif } public override void Initialize(Harmony plibInstance) { Instance = this; // Non essential, do not crash on fail try { plibInstance.Patch(typeof(BuildingTemplates), nameof(BuildingTemplates. CreateBuildingDef), postfix: PatchMethod(nameof( CreateBuildingDef_Postfix))); plibInstance.Patch(typeof(EquipmentTemplates), nameof(EquipmentTemplates. CreateEquipmentDef), postfix: PatchMethod(nameof( CreateEquipmentDef_Postfix))); } catch (Exception e) { #if DEBUG PUtil.LogExcWarn(e); #endif } plibInstance.Patch(typeof(GeneratedBuildings), nameof(GeneratedBuildings. LoadGeneratedBuildings), prefix: PatchMethod(nameof( LoadGeneratedBuildings_Prefix))); // Avoid another Harmony patch by using PatchManager var pm = new PPatchManager(plibInstance); pm.RegisterPatch(RunAt.AfterDbInit, new BuildingTechRegistration()); } public override void Process(uint operation, object _) { if (operation == 0) AddStrings(); else if (operation == 1) AddTechs(); } /// /// A Patch Manager patch which registers all PBuilding technologies. /// private sealed class BuildingTechRegistration : IPatchMethodInstance { public void Run(Harmony instance) { Instance?.AddAllTechs(); } } } }