using System.Collections.Generic; using GameFramework.DataTable; using GeometryTD.DataTable; using GeometryTD.Definition; using GeometryTD.Factory; using GeometryTD.UI; using UnityEngine; using UnityGameFramework.Runtime; using Random = System.Random; namespace GeometryTD.CustomComponent { public sealed class InventoryGenerationComponent : GameFrameworkComponent { private readonly List _shopPriceRows = new(); private IDataTable _shopPriceTable; private IDataTable _dropPoolTable; private IDataTable _muzzleCompTable; private IDataTable _bearingCompTable; private IDataTable _baseCompTable; private ShopGoodsBuilder _shopGoodsBuilder; private DropPoolRoller _dropPoolRoller; private RewardCandidateBuilder _rewardCandidateBuilder; private OutGameDropItemBuilder _outGameDropItemBuilder; public List BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1) { EnsureShopBuilder(); return _shopGoodsBuilder.BuildGoods(goodsCount, runSeed, sequenceIndex); } public EnemyDropResult ResolveEnemyDrop( in EnemyDropContext context, int runSeed, int sequenceIndex, ref int nextDropOrdinal) { DREnemy enemy = context.Enemy; if (enemy == null) { return EnemyDropResult.Empty; } int coin = Mathf.Max(0, enemy.DropCoin); int gold = 0; float dropRate = enemy.DropPercent > 1f ? Mathf.Clamp01(enemy.DropPercent * 0.01f) : Mathf.Clamp01(enemy.DropPercent); int dropOrdinal = nextDropOrdinal; nextDropOrdinal++; InventoryGenerationRandomContext randomContext = new(runSeed, sequenceIndex, InventoryTagSourceType.Drop, dropOrdinal); Random random = randomContext.CreateRandom(); if (enemy.DropGold > 0 && dropRate > 0f && random.NextDouble() <= dropRate) { gold = Mathf.Max(0, enemy.DropGold); } TowerCompItemData lootItem = null; if (OutGameDropRuleService.ShouldRollOutGameItem(context.DisplayPhaseIndex, random) && TryRollOutGameItem( context.DisplayPhaseIndex, context.ThemeType, randomContext, random, out TowerCompItemData droppedItem)) { lootItem = droppedItem; } return new EnemyDropResult(coin, gold, lootItem); } public IReadOnlyList BuildRewardCandidates( int displayPhaseIndex, LevelThemeType themeType, int candidateCount, int runSeed, int sequenceIndex, ref int nextRewardOrdinal) { RewardCandidateBuilder rewardCandidateBuilder = EnsureRewardCandidateBuilder(); int rewardOrdinal = nextRewardOrdinal; IReadOnlyList candidates = rewardCandidateBuilder.BuildCandidates( displayPhaseIndex, themeType, candidateCount, CreateNextRewardRandomContext, BuildRewardCandidateItem); nextRewardOrdinal = rewardOrdinal; return candidates; InventoryGenerationRandomContext CreateNextRewardRandomContext() { return new InventoryGenerationRandomContext( runSeed, sequenceIndex, InventoryTagSourceType.Reward, rewardOrdinal++); } } 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 System.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 System.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 System.InvalidOperationException("InventoryGenerationComponent requires non-null shop price rows."); } } private void EnsureShopBuilder() { EnsureShopTables(); _shopGoodsBuilder ??= new ShopGoodsBuilder( _shopPriceRows, _muzzleCompTable, _bearingCompTable, _baseCompTable); } private void EnsureDropTables() { _dropPoolTable ??= GameEntry.DataTable.GetDataTable(); _muzzleCompTable ??= GameEntry.DataTable.GetDataTable(); _bearingCompTable ??= GameEntry.DataTable.GetDataTable(); _baseCompTable ??= GameEntry.DataTable.GetDataTable(); if (_dropPoolTable == null || _muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null) { throw new System.InvalidOperationException( "InventoryGenerationComponent requires OutGameDropPool, MuzzleComp, BearingComp, and BaseComp data tables."); } } private DropPoolRoller EnsureDropPoolRoller() { EnsureDropTables(); _dropPoolRoller ??= new DropPoolRoller(_dropPoolTable); return _dropPoolRoller; } private RewardCandidateBuilder EnsureRewardCandidateBuilder() { _rewardCandidateBuilder ??= new RewardCandidateBuilder(EnsureDropPoolRoller()); return _rewardCandidateBuilder; } private OutGameDropItemBuilder EnsureOutGameDropItemBuilder() { EnsureDropTables(); _outGameDropItemBuilder ??= new OutGameDropItemBuilder( _muzzleCompTable, _bearingCompTable, _baseCompTable); return _outGameDropItemBuilder; } private bool TryRollOutGameItem( int displayPhaseIndex, LevelThemeType themeType, InventoryGenerationRandomContext randomContext, Random random, out TowerCompItemData droppedItem) { droppedItem = null; DropPoolRoller dropPoolRoller = EnsureDropPoolRoller(); int phaseIndex = Mathf.Max(1, displayPhaseIndex); if (!dropPoolRoller.TryRollRow( phaseIndex, themeType, random, out DROutGameDropPool selectedRow, out RarityType selectedRarity) || selectedRow == null) { return false; } return EnsureOutGameDropItemBuilder().TryBuildItem(selectedRow, selectedRarity, randomContext, out droppedItem); } private TowerCompItemData BuildRewardCandidateItem( DROutGameDropPool row, RarityType rarity, InventoryGenerationRandomContext randomContext) { if (!EnsureOutGameDropItemBuilder().TryBuildItem(row, rarity, randomContext, out TowerCompItemData droppedItem)) { return null; } return droppedItem; } } }