using System; using System.Collections.Generic; using GameFramework.DataTable; using GeometryTD.CustomUtility; using GeometryTD.DataTable; using GeometryTD.Definition; using GeometryTD.Factory; using GeometryTD.UI; using UnityEngine; using UnityGameFramework.Runtime; namespace GeometryTD.CustomComponent { public sealed class InventoryGenerationComponent : GameFrameworkComponent { private long _nextTempInstanceId = 1000000; private readonly List _shopPriceRows = new(); private IDataTable _shopPriceTable; private IDataTable _muzzleCompTable; private IDataTable _bearingCompTable; private IDataTable _baseCompTable; public List BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1) { if (goodsCount <= 0) { return new List(); } EnsureShopTables(); List goodsItems = new(goodsCount); for (int i = 0; i < goodsCount; i++) { goodsItems.Add(BuildShopGoodsItem(i, runSeed, sequenceIndex)); } return goodsItems; } public EnemyDropResult ResolveEnemyDrop(in EnemyDropContext context) { throw new NotImplementedException( "InventoryGenerationComponent.ResolveEnemyDrop() will be implemented in G2."); } public IReadOnlyList BuildRewardCandidates( int displayPhaseIndex, LevelThemeType themeType, int candidateCount) { throw new NotImplementedException( "InventoryGenerationComponent.BuildRewardCandidates() will be implemented in G2."); } private void EnsureShopTables() { _shopPriceTable ??= GameEntry.DataTable.GetDataTable(); _muzzleCompTable ??= GameEntry.DataTable.GetDataTable(); _bearingCompTable ??= GameEntry.DataTable.GetDataTable(); _baseCompTable ??= GameEntry.DataTable.GetDataTable(); if (_shopPriceTable == null || _muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null) { throw new InvalidOperationException( "InventoryGenerationComponent requires ShopPrice, MuzzleComp, BearingComp, and BaseComp data tables."); } if (_shopPriceRows.Count > 0) { return; } DRShopPrice[] rows = _shopPriceTable.GetAllDataRows(); if (rows == null || rows.Length <= 0) { throw new InvalidOperationException( "InventoryGenerationComponent requires at least one shop price row."); } foreach (var row in rows) { if (row != null) { _shopPriceRows.Add(row); } } if (_shopPriceRows.Count <= 0) { throw new InvalidOperationException("InventoryGenerationComponent requires non-null shop price rows."); } } private GoodsItemRawData BuildShopGoodsItem(int goodsIndex, int runSeed, int sequenceIndex) { TowerCompItemData sourceItem = BuildRandomComponentItem(goodsIndex, runSeed, sequenceIndex); return new GoodsItemRawData { GoodsIndex = goodsIndex, Title = sourceItem.Name, TypeText = BuildTypeText(sourceItem.SlotType), Description = BuildDescription(sourceItem), Price = ResolveRandomPrice(sourceItem.Rarity), Tags = sourceItem.Tags != null ? (TagType[])sourceItem.Tags.Clone() : Array.Empty(), IconAreaContext = BuildIconAreaContext(sourceItem), SourceItem = sourceItem, IsPurchased = false }; } private TowerCompItemData BuildRandomComponentItem(int goodsIndex, int runSeed, int sequenceIndex) { int slotRoll = UnityEngine.Random.Range(0, 3); DRShopPrice priceRow = _shopPriceRows[UnityEngine.Random.Range(0, _shopPriceRows.Count)]; RarityType rarity = InventoryRarityRuleService.NormalizeComponentRarity( priceRow != null ? priceRow.Rarity : RarityType.White); return slotRoll switch { 0 => BuildRandomMuzzleItem(rarity, goodsIndex, runSeed, sequenceIndex), 1 => BuildRandomBearingItem(rarity, goodsIndex, runSeed, sequenceIndex), _ => BuildRandomBaseItem(rarity, goodsIndex, runSeed, sequenceIndex) }; } private MuzzleCompItemData BuildRandomMuzzleItem(RarityType rarity, int goodsIndex, int runSeed, int sequenceIndex) { DRMuzzleComp[] rows = _muzzleCompTable.GetAllDataRows(); DRMuzzleComp config = rows[UnityEngine.Random.Range(0, rows.Length)]; long instanceId = _nextTempInstanceId++; InventoryTagRandomContext randomContext = InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id); return ComponentItemFactory.CreateMuzzle(config, instanceId, rarity, randomContext); } private BearingCompItemData BuildRandomBearingItem(RarityType rarity, int goodsIndex, int runSeed, int sequenceIndex) { DRBearingComp[] rows = _bearingCompTable.GetAllDataRows(); DRBearingComp config = rows[UnityEngine.Random.Range(0, rows.Length)]; long instanceId = _nextTempInstanceId++; InventoryTagRandomContext randomContext = InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id); return ComponentItemFactory.CreateBearing(config, instanceId, rarity, randomContext); } private BaseCompItemData BuildRandomBaseItem(RarityType rarity, int goodsIndex, int runSeed, int sequenceIndex) { DRBaseComp[] rows = _baseCompTable.GetAllDataRows(); DRBaseComp config = rows[UnityEngine.Random.Range(0, rows.Length)]; long instanceId = _nextTempInstanceId++; InventoryTagRandomContext randomContext = InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id); return ComponentItemFactory.CreateBase(config, instanceId, rarity, randomContext); } private int ResolveRandomPrice(RarityType rarity) { for (int i = 0; i < _shopPriceRows.Count; i++) { DRShopPrice row = _shopPriceRows[i]; if (row != null && row.Rarity == rarity) { int min = Mathf.Max(0, row.MinPrice); int max = Mathf.Max(min, row.MaxPrice); return UnityEngine.Random.Range(min, max + 1); } } return 0; } private static IconAreaContext BuildIconAreaContext(TowerCompItemData item) { return new IconAreaContext { Rarity = item.Rarity, ComponentSlotType = item.SlotType, Color = IconColorGenerator.GenerateForComponent(item) }; } private static string BuildTypeText(TowerCompSlotType slotType) { return slotType switch { TowerCompSlotType.Muzzle => "枪口组件", TowerCompSlotType.Bearing => "轴承组件", TowerCompSlotType.Base => "底座组件", _ => "组件" }; } private static string BuildDescription(TowerCompItemData item) { if (item is MuzzleCompItemData muzzleComp) { return ItemDescUtility.BuildMuzzleDesc(muzzleComp); } if (item is BearingCompItemData bearingComp) { return ItemDescUtility.BuildBearingDesc(bearingComp); } if (item is BaseCompItemData baseComp) { return ItemDescUtility.BuildBaseDesc(baseComp); } return string.Empty; } } }