241 lines
8.5 KiB
C#
241 lines
8.5 KiB
C#
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 int _runSeed;
|
|
private int _nodeSequenceIndex = -1;
|
|
private int _nextDropOrdinal;
|
|
private int _nextRewardOrdinal;
|
|
|
|
private readonly List<DRShopPrice> _shopPriceRows = new();
|
|
private IDataTable<DRShopPrice> _shopPriceTable;
|
|
private IDataTable<DROutGameDropPool> _dropPoolTable;
|
|
private IDataTable<DRMuzzleComp> _muzzleCompTable;
|
|
private IDataTable<DRBearingComp> _bearingCompTable;
|
|
private IDataTable<DRBaseComp> _baseCompTable;
|
|
private ShopGoodsBuilder _shopGoodsBuilder;
|
|
private DropPoolRoller _dropPoolRoller;
|
|
private RewardCandidateBuilder _rewardCandidateBuilder;
|
|
private OutGameDropItemBuilder _outGameDropItemBuilder;
|
|
|
|
public void ConfigureRunContext(int runSeed, int sequenceIndex)
|
|
{
|
|
_runSeed = runSeed;
|
|
_nodeSequenceIndex = sequenceIndex;
|
|
_nextDropOrdinal = 0;
|
|
_nextRewardOrdinal = 0;
|
|
}
|
|
|
|
public List<GoodsItemRawData> BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1)
|
|
{
|
|
EnsureShopBuilder();
|
|
return _shopGoodsBuilder.BuildGoods(goodsCount, runSeed, sequenceIndex);
|
|
}
|
|
|
|
public EnemyDropResult ResolveEnemyDrop(in EnemyDropContext context)
|
|
{
|
|
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 = AllocateDropOrdinal();
|
|
InventoryGenerationRandomContext randomContext =
|
|
new(_runSeed, _nodeSequenceIndex, 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<TowerCompItemData> BuildRewardCandidates(
|
|
int displayPhaseIndex,
|
|
LevelThemeType themeType,
|
|
int candidateCount)
|
|
{
|
|
RewardCandidateBuilder rewardCandidateBuilder = EnsureRewardCandidateBuilder();
|
|
return rewardCandidateBuilder.BuildCandidates(
|
|
displayPhaseIndex,
|
|
themeType,
|
|
candidateCount,
|
|
CreateNextRewardRandomContext,
|
|
BuildRewardCandidateItem);
|
|
}
|
|
|
|
private void EnsureShopTables()
|
|
{
|
|
_shopPriceTable ??= GameEntry.DataTable.GetDataTable<DRShopPrice>();
|
|
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
|
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
|
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
|
|
|
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<DROutGameDropPool>();
|
|
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
|
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
|
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
|
|
|
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 int AllocateDropOrdinal()
|
|
{
|
|
return _nextDropOrdinal++;
|
|
}
|
|
|
|
private int AllocateRewardOrdinal()
|
|
{
|
|
return _nextRewardOrdinal++;
|
|
}
|
|
|
|
private InventoryGenerationRandomContext CreateNextRewardRandomContext()
|
|
{
|
|
return new InventoryGenerationRandomContext(
|
|
_runSeed,
|
|
_nodeSequenceIndex,
|
|
InventoryTagSourceType.Reward,
|
|
AllocateRewardOrdinal());
|
|
}
|
|
|
|
private TowerCompItemData BuildRewardCandidateItem(
|
|
DROutGameDropPool row,
|
|
RarityType rarity,
|
|
InventoryGenerationRandomContext randomContext)
|
|
{
|
|
if (!EnsureOutGameDropItemBuilder().TryBuildItem(row, rarity, randomContext, out TowerCompItemData droppedItem))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return droppedItem;
|
|
}
|
|
}
|
|
}
|