geometry-tower-defense/Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/InventoryGenerationComponen...

306 lines
11 KiB
C#

using System;
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 = UnityEngine.Random;
namespace GeometryTD.CustomComponent
{
public sealed class InventoryGenerationComponent : GameFrameworkComponent
{
private const float DropChanceBase = 0.05f;
private const float DropChancePerPhase = 0.2f;
private const float DropChanceCap = 0.2f;
private long _nextTempInstanceId = 1000000;
private int _runSeed;
private int _nodeSequenceIndex = -1;
private int _nextDropTagOrdinal;
private int _nextRewardTagOrdinal;
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;
public void ConfigureRunContext(int runSeed, int sequenceIndex)
{
_runSeed = runSeed;
_nodeSequenceIndex = sequenceIndex;
_nextDropTagOrdinal = 0;
_nextRewardTagOrdinal = 0;
}
public List<GoodsItemRawData> BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1)
{
EnsureShopBuilder();
return _shopGoodsBuilder.BuildGoods(goodsCount, runSeed, sequenceIndex, AllocateTempInstanceId);
}
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);
if (enemy.DropGold > 0 && dropRate > 0f && Random.value <= dropRate)
{
gold = Mathf.Max(0, enemy.DropGold);
}
TowerCompItemData lootItem = null;
if (ShouldRollOutGameItem(context.DisplayPhaseIndex) &&
TryRollOutGameItem(context.DisplayPhaseIndex, context.ThemeType, 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,
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 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 void EnsureShopBuilder()
{
EnsureShopTables();
_shopGoodsBuilder ??= new ShopGoodsBuilder(
_shopPriceRows,
_muzzleCompTable,
_bearingCompTable,
_baseCompTable);
}
private long AllocateTempInstanceId()
{
return _nextTempInstanceId++;
}
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 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 static bool ShouldRollOutGameItem(int displayPhaseIndex)
{
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
float dropChance = Mathf.Clamp(DropChanceBase + (phaseIndex - 1) * DropChancePerPhase, 0f, DropChanceCap);
return Random.value <= dropChance;
}
private bool TryRollOutGameItem(int displayPhaseIndex, LevelThemeType themeType, out TowerCompItemData droppedItem)
{
droppedItem = null;
DropPoolRoller dropPoolRoller = EnsureDropPoolRoller();
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
if (!dropPoolRoller.TryRollRow(
phaseIndex,
themeType,
out DROutGameDropPool selectedRow,
out RarityType selectedRarity) || selectedRow == null)
{
return false;
}
return TryBuildDropItem(
selectedRow,
selectedRarity,
InventoryTagSourceType.Drop,
AllocateDropTagOrdinal(),
out droppedItem);
}
private bool TryBuildDropItem(
DROutGameDropPool row,
RarityType rarity,
InventoryTagSourceType sourceType,
int localOrdinal,
out TowerCompItemData droppedItem)
{
droppedItem = null;
if (row.ItemId <= 0 || string.IsNullOrWhiteSpace(row.ItemType))
{
return false;
}
string itemType = row.ItemType.Trim();
if (itemType.Equals("MuzzleComp", StringComparison.OrdinalIgnoreCase))
{
DRMuzzleComp config = _muzzleCompTable.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = ComponentItemFactory.CreateMuzzle(
config,
AllocateTempInstanceId(),
rarity,
CreateRandomContext(sourceType, localOrdinal, config.Id));
return true;
}
if (itemType.Equals("BearingComp", StringComparison.OrdinalIgnoreCase))
{
DRBearingComp config = _bearingCompTable.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = ComponentItemFactory.CreateBearing(
config,
AllocateTempInstanceId(),
rarity,
CreateRandomContext(sourceType, localOrdinal, config.Id));
return true;
}
if (itemType.Equals("BaseComp", StringComparison.OrdinalIgnoreCase))
{
DRBaseComp config = _baseCompTable.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = ComponentItemFactory.CreateBase(
config,
AllocateTempInstanceId(),
rarity,
CreateRandomContext(sourceType, localOrdinal, config.Id));
return true;
}
return false;
}
private int AllocateDropTagOrdinal()
{
return _nextDropTagOrdinal++;
}
private int AllocateRewardTagOrdinal()
{
return _nextRewardTagOrdinal++;
}
private TowerCompItemData BuildRewardCandidateItem(DROutGameDropPool row, RarityType rarity)
{
if (!TryBuildDropItem(
row,
rarity,
InventoryTagSourceType.Reward,
AllocateRewardTagOrdinal(),
out TowerCompItemData droppedItem))
{
return null;
}
return droppedItem;
}
private InventoryTagRandomContext CreateRandomContext(
InventoryTagSourceType sourceType,
int localOrdinal,
int configId)
{
return sourceType switch
{
InventoryTagSourceType.Reward => InventoryTagRandomContext.CreateReward(_runSeed, _nodeSequenceIndex, localOrdinal, configId),
_ => InventoryTagRandomContext.CreateDrop(_runSeed, _nodeSequenceIndex, localOrdinal, configId)
};
}
}
}