using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; using Core.Models.Utils; namespace Core.Services; [Injectable(InjectionType.Singleton)] public class ItemBaseClassService( ISptLogger _logger, DatabaseService _databaseService, LocalisationService _localisationService ) { private bool _cacheGenerated; private Dictionary> _itemBaseClassesCache; /** * Create cache and store inside ItemBaseClassService * Store a dict of an items tpl to the base classes it and its parents have */ public void HydrateItemBaseClassCache() { // Clear existing cache _itemBaseClassesCache = new Dictionary>(); var items = _databaseService.GetItems(); var filteredDbItems = (items).Where((x) => x.Value.Type == "Item"); foreach (var item in filteredDbItems) { var itemIdToUpdate = item.Value.Id; if (!_itemBaseClassesCache.ContainsKey(item.Value.Id)) { _itemBaseClassesCache[item.Value.Id] = []; } AddBaseItems(itemIdToUpdate, item.Value); } _cacheGenerated = true; } /** * Helper method, recursively iterate through items parent items, finding and adding ids to dictionary * @param itemIdToUpdate item tpl to store base ids against in dictionary * @param item item being checked */ protected void AddBaseItems(string itemIdToUpdate, TemplateItem item) { _itemBaseClassesCache[itemIdToUpdate].Add(item.Parent); var parent = _databaseService.GetItems()[item.Parent]; if (parent.Parent != "") { AddBaseItems(itemIdToUpdate, parent); } } /** * Does item tpl inherit from the requested base class * @param itemTpl item to check base classes of * @param baseClass base class to check for * @returns true if item inherits from base class passed in */ public bool ItemHasBaseClass(string itemTpl, List baseClasses) { if (!_cacheGenerated) { HydrateItemBaseClassCache(); } if (itemTpl is null) { _logger.Warning("Unable to check itemTpl base class as value passed is null"); return false; } // The cache is only generated for item templates with `_type === "Item"`, so return false for any other type, // including item templates that simply don't exist. if (!CachedItemIsOfItemType(itemTpl)) { return false; } // No item in cache if (_itemBaseClassesCache.ContainsKey(itemTpl)) { return _itemBaseClassesCache[itemTpl].Any(baseClasses.Contains); } _logger.Debug(_localisationService.GetText("baseclass-item_not_found", itemTpl)); // Not found in cache, Hydrate again - some mods add items late HydrateItemBaseClassCache(); // Check for item again, return false if item not found a second time if (_itemBaseClassesCache.ContainsKey(itemTpl)) { return _itemBaseClassesCache[itemTpl].Any(baseClasses.Contains); } _logger.Warning(_localisationService.GetText("baseclass-item_not_found_failed", itemTpl)); return false; } /** * Check if cached item template is of type Item * @param itemTemplateId item to check * @returns true if item is of type Item */ private bool CachedItemIsOfItemType(string itemTemplateId) { return _databaseService.GetItems()[itemTemplateId]?.Type == "Item"; } /** * Get base classes item inherits from * @param itemTpl item to get base classes for * @returns array of base classes */ public List GetItemBaseClasses(string itemTpl) { if (!_cacheGenerated) { HydrateItemBaseClassCache(); } if (!_itemBaseClassesCache.ContainsKey(itemTpl)) { return []; } return _itemBaseClassesCache[itemTpl]; } }