geometry-tower-defense/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatResourceManager.cs

399 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using UnityEngine;
using Random = UnityEngine.Random;
namespace GeometryTD.CustomComponent
{
internal sealed class CombatResourceManager
{
private const float DropChanceBase = 0.05f;
private const float DropChancePerPhase = 0.0125f;
private const float DropChanceCap = 0.45f;
private const float RarityCurveScalePhase = 30f;
private readonly List<DROutGameDropPool> _eligibleDropPoolBuffer = new();
private readonly Dictionary<RarityType, float> _rarityRollWeightBuffer = new();
private readonly BackpackInventoryData _rewardInventory = new();
private IDataTable<DROutGameDropPool> _drOutGameDropPool;
private IDataTable<DRMuzzleComp> _drMuzzleComp;
private IDataTable<DRBearingComp> _drBearingComp;
private IDataTable<DRBaseComp> _drBaseComp;
private long _nextDropItemInstanceId = 1;
public int GainedCoin { get; private set; }
public int GainedGold { get; private set; }
public void Reset()
{
GainedCoin = 0;
GainedGold = 0;
_rewardInventory.Gold = 0;
_rewardInventory.MuzzleComponents.Clear();
_rewardInventory.BearingComponents.Clear();
_rewardInventory.BaseComponents.Clear();
_rewardInventory.Towers.Clear();
_nextDropItemInstanceId = 1;
}
public BackpackInventoryData GetRewardInventorySnapshot()
{
return new BackpackInventoryData
{
Gold = _rewardInventory.Gold,
MuzzleComponents = new List<MuzzleCompItemData>(_rewardInventory.MuzzleComponents),
BearingComponents = new List<BearingCompItemData>(_rewardInventory.BearingComponents),
BaseComponents = new List<BaseCompItemData>(_rewardInventory.BaseComponents),
Towers = new List<DefenseTowerItemData>(_rewardInventory.Towers)
};
}
public void AddEnemyDefeatedReward(int gainedCoin, int gainedGold)
{
int coin = Mathf.Max(0, gainedCoin);
int gold = Mathf.Max(0, gainedGold);
GainedCoin += coin;
GainedGold += gold;
if (gold > 0)
{
_rewardInventory.Gold += gold;
}
GameEntry.CombatNode?.ApplyEnemyDropReward(coin, gold);
}
public bool TryRollOutGameItemDrop(int displayPhaseIndex, LevelThemeType themeType)
{
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
float dropChance = Mathf.Clamp(DropChanceBase + (phaseIndex - 1) * DropChancePerPhase, 0f, DropChanceCap);
if (Random.value > dropChance)
{
return false;
}
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow))
{
return false;
}
if (!TryBuildDropItem(selectedRow, out TowerCompItemData droppedItem))
{
return false;
}
if (droppedItem is MuzzleCompItemData muzzleCompItemData)
{
_rewardInventory.MuzzleComponents.Add(muzzleCompItemData);
return true;
}
if (droppedItem is BearingCompItemData bearingCompItemData)
{
_rewardInventory.BearingComponents.Add(bearingCompItemData);
return true;
}
if (droppedItem is BaseCompItemData baseCompItemData)
{
_rewardInventory.BaseComponents.Add(baseCompItemData);
return true;
}
return false;
}
private bool TryPickDropPoolRow(int displayPhaseIndex, LevelThemeType themeType, out DROutGameDropPool selectedRow)
{
selectedRow = null;
IDataTable<DROutGameDropPool> dropTable = EnsureOutGameDropPoolTable();
if (dropTable == null)
{
return false;
}
_eligibleDropPoolBuffer.Clear();
DROutGameDropPool[] allRows = dropTable.GetAllDataRows();
for (int i = 0; i < allRows.Length; i++)
{
DROutGameDropPool row = allRows[i];
if (row == null)
{
continue;
}
if (row.LevelThemeType != themeType)
{
continue;
}
if (displayPhaseIndex < row.MinPhase || displayPhaseIndex > row.MaxPhase)
{
continue;
}
_eligibleDropPoolBuffer.Add(row);
}
if (_eligibleDropPoolBuffer.Count <= 0)
{
return false;
}
RarityType selectedRarity = RollRarity(displayPhaseIndex, _eligibleDropPoolBuffer);
if (selectedRarity == RarityType.None)
{
return false;
}
int totalWeight = 0;
for (int i = 0; i < _eligibleDropPoolBuffer.Count; i++)
{
DROutGameDropPool row = _eligibleDropPoolBuffer[i];
if (row.Rarity != selectedRarity)
{
continue;
}
totalWeight += Mathf.Max(1, row.Weight);
}
if (totalWeight <= 0)
{
return false;
}
int randomWeight = Random.Range(1, totalWeight + 1);
int cumulativeWeight = 0;
for (int i = 0; i < _eligibleDropPoolBuffer.Count; i++)
{
DROutGameDropPool row = _eligibleDropPoolBuffer[i];
if (row.Rarity != selectedRarity)
{
continue;
}
cumulativeWeight += Mathf.Max(1, row.Weight);
if (randomWeight <= cumulativeWeight)
{
selectedRow = row;
return true;
}
}
selectedRow = _eligibleDropPoolBuffer[_eligibleDropPoolBuffer.Count - 1];
return selectedRow != null;
}
private RarityType RollRarity(int displayPhaseIndex, List<DROutGameDropPool> candidates)
{
_rarityRollWeightBuffer.Clear();
float phaseT = Mathf.Clamp01((displayPhaseIndex - 1) / RarityCurveScalePhase);
for (int i = 0; i < candidates.Count; i++)
{
DROutGameDropPool row = candidates[i];
if (row == null)
{
continue;
}
float curveWeight = GetRarityCurveWeight(row.Rarity, phaseT);
if (curveWeight <= 0f)
{
continue;
}
if (_rarityRollWeightBuffer.TryGetValue(row.Rarity, out float existingWeight))
{
_rarityRollWeightBuffer[row.Rarity] = existingWeight + Mathf.Max(1, row.Weight) * curveWeight;
}
else
{
_rarityRollWeightBuffer[row.Rarity] = Mathf.Max(1, row.Weight) * curveWeight;
}
}
float totalWeight = 0f;
foreach (var pair in _rarityRollWeightBuffer)
{
totalWeight += Mathf.Max(0f, pair.Value);
}
if (totalWeight <= 0f)
{
return RarityType.None;
}
float randomWeight = Random.value * totalWeight;
float cumulativeWeight = 0f;
foreach (var pair in _rarityRollWeightBuffer)
{
cumulativeWeight += Mathf.Max(0f, pair.Value);
if (randomWeight <= cumulativeWeight)
{
return pair.Key;
}
}
foreach (var pair in _rarityRollWeightBuffer)
{
return pair.Key;
}
return RarityType.None;
}
private static float GetRarityCurveWeight(RarityType rarityType, float phaseT)
{
float hump = Mathf.Exp(-Mathf.Pow((phaseT - 0.35f) / 0.28f, 2f));
switch (rarityType)
{
case RarityType.White:
return Mathf.Max(0.05f, 0.18f + 1.25f * hump);
case RarityType.Green:
return Mathf.Max(0.05f, 0.35f + 0.55f * hump);
case RarityType.Blue:
return 0.18f + 0.55f * phaseT;
case RarityType.Purple:
return 0.05f + 0.22f * phaseT;
case RarityType.Red:
return 0.01f + 0.08f * phaseT * phaseT;
default:
return 0f;
}
}
private IDataTable<DROutGameDropPool> EnsureOutGameDropPoolTable()
{
if (_drOutGameDropPool != null)
{
return _drOutGameDropPool;
}
_drOutGameDropPool = GameEntry.DataTable.GetDataTable<DROutGameDropPool>();
return _drOutGameDropPool;
}
private bool TryBuildDropItem(DROutGameDropPool row, out TowerCompItemData droppedItem)
{
droppedItem = null;
if (row == null || row.ItemId <= 0 || string.IsNullOrWhiteSpace(row.ItemType))
{
return false;
}
string itemType = row.ItemType.Trim();
if (itemType.Equals("MuzzleComp", StringComparison.OrdinalIgnoreCase))
{
return TryBuildMuzzleCompItem(row, out droppedItem);
}
if (itemType.Equals("BearingComp", StringComparison.OrdinalIgnoreCase))
{
return TryBuildBearingCompItem(row, out droppedItem);
}
if (itemType.Equals("BaseComp", StringComparison.OrdinalIgnoreCase))
{
return TryBuildBaseCompItem(row, out droppedItem);
}
return false;
}
private bool TryBuildMuzzleCompItem(DROutGameDropPool row, out TowerCompItemData droppedItem)
{
droppedItem = null;
_drMuzzleComp ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
if (_drMuzzleComp == null)
{
return false;
}
DRMuzzleComp config = _drMuzzleComp.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = new MuzzleCompItemData
{
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
AttackDamage = config.AttackDamage != null ? (int[])config.AttackDamage.Clone() : Array.Empty<int>(),
DamageRandomRate = config.DamageRandomRate,
AttackMethodType = config.AttackMethodType
};
return true;
}
private bool TryBuildBearingCompItem(DROutGameDropPool row, out TowerCompItemData droppedItem)
{
droppedItem = null;
_drBearingComp ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
if (_drBearingComp == null)
{
return false;
}
DRBearingComp config = _drBearingComp.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = new BearingCompItemData
{
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
RotateSpeed = config.RotateSpeed != null ? (float[])config.RotateSpeed.Clone() : Array.Empty<float>(),
AttackRange = config.AttackRange != null ? (float[])config.AttackRange.Clone() : Array.Empty<float>()
};
return true;
}
private bool TryBuildBaseCompItem(DROutGameDropPool row, out TowerCompItemData droppedItem)
{
droppedItem = null;
_drBaseComp ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
if (_drBaseComp == null)
{
return false;
}
DRBaseComp config = _drBaseComp.GetDataRow(row.ItemId);
if (config == null)
{
return false;
}
droppedItem = new BaseCompItemData
{
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
AttackSpeed = config.AttackSpeed != null ? (float[])config.AttackSpeed.Clone() : Array.Empty<float>(),
AttackPropertyType = config.AttackPropertyType
};
return true;
}
}
}