refactor 8:
- CombatScheduler 不再反向访问 GameEntry.CombatNode。现在由 CombatNodeComponent.cs:322 在初始化时把完成回调传进 scheduler,运行时回调保存在 CombatSchedulerRuntimeContext.cs:27,完成时由 CombatSchedulerFlowCoordinator.cs:245 触发;主题解析也只看当前战斗上下文,不再回退查 facade,见 CombatSchedulerFlowCoordinator.cs:161。同时把 MapEntity 的 coin 回调改成直接连到资源真值来源 CombatLoadingState.cs:56,并把 ICombatSchedulerHost 上那两个资源转发接口删掉,见 ICombatSchedulerHost.cs:5。 - 地图加载入口也收紧了。CombatLoadSession 现在要求必须有 MapEntityLoadContext.InitialMapData,不再走隐式兜底,见 CombatLoadSession.cs:227。EntityExtension 删除了 ShowMap(MapData) 旧重载,当前只保留 ShowMap(MapEntityLoadContext),见 EntityExtension.cs:52;MapEntity 也不再接受裸 MapData 的遗留分支,见 MapEntity.cs:263。
This commit is contained in:
+6
-4
@@ -1,20 +1,22 @@
|
||||
using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal readonly struct EnemyDropResolveResult
|
||||
{
|
||||
public static EnemyDropResolveResult Empty => new(0, 0, false);
|
||||
public static EnemyDropResolveResult Empty => new(0, 0, null);
|
||||
|
||||
public EnemyDropResolveResult(int coin, int gold, bool shouldRollOutGameItem)
|
||||
public EnemyDropResolveResult(int coin, int gold, TowerCompItemData lootItem)
|
||||
{
|
||||
Coin = coin;
|
||||
Gold = gold;
|
||||
ShouldRollOutGameItem = shouldRollOutGameItem;
|
||||
LootItem = lootItem;
|
||||
}
|
||||
|
||||
public int Coin { get; }
|
||||
|
||||
public int Gold { get; }
|
||||
|
||||
public bool ShouldRollOutGameItem { get; }
|
||||
public TowerCompItemData LootItem { get; }
|
||||
}
|
||||
}
|
||||
|
||||
+394
-1
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.DataTable;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
@@ -6,6 +10,27 @@ namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class EnemyDropResolver
|
||||
{
|
||||
private const float DropChanceBase = 0.05f;
|
||||
private const float DropChancePerPhase = 0.2f;
|
||||
private const float DropChanceCap = 0.2f;
|
||||
private const float RarityCurveScalePhase = 30f;
|
||||
|
||||
private readonly List<DROutGameDropPool> _eligibleDropPoolBuffer = new();
|
||||
private readonly Dictionary<RarityType, float> _rarityRollWeightBuffer = new();
|
||||
|
||||
private IDataTable<DROutGameDropPool> _drOutGameDropPool;
|
||||
private IDataTable<DRMuzzleComp> _drMuzzleComp;
|
||||
private IDataTable<DRBearingComp> _drBearingComp;
|
||||
private IDataTable<DRBaseComp> _drBaseComp;
|
||||
private long _nextDropItemInstanceId = 1;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_eligibleDropPoolBuffer.Clear();
|
||||
_rarityRollWeightBuffer.Clear();
|
||||
_nextDropItemInstanceId = 1;
|
||||
}
|
||||
|
||||
public EnemyDropResolveResult Resolve(in EnemyDropResolveContext context)
|
||||
{
|
||||
DREnemy enemy = context.Enemy;
|
||||
@@ -26,7 +51,375 @@ namespace GeometryTD.CustomComponent
|
||||
gold = Mathf.Max(0, enemy.DropGold);
|
||||
}
|
||||
|
||||
return new EnemyDropResolveResult(coin, gold, shouldRollOutGameItem: true);
|
||||
TowerCompItemData lootItem = null;
|
||||
if (ShouldRollOutGameItem(context.DisplayPhaseIndex) &&
|
||||
TryRollOutGameItem(context.DisplayPhaseIndex, context.ThemeType, out TowerCompItemData droppedItem))
|
||||
{
|
||||
lootItem = droppedItem;
|
||||
}
|
||||
|
||||
return new EnemyDropResolveResult(coin, gold, lootItem);
|
||||
}
|
||||
|
||||
public IReadOnlyList<TowerCompItemData> RollSettlementRewardCandidates(
|
||||
int displayPhaseIndex,
|
||||
LevelThemeType themeType,
|
||||
int candidateCount)
|
||||
{
|
||||
int resolvedCount = Mathf.Max(0, candidateCount);
|
||||
if (resolvedCount <= 0)
|
||||
{
|
||||
return Array.Empty<TowerCompItemData>();
|
||||
}
|
||||
|
||||
List<TowerCompItemData> candidates = new List<TowerCompItemData>(resolvedCount);
|
||||
HashSet<int> selectedPoolRowIds = new HashSet<int>();
|
||||
int maxAttempts = Mathf.Max(resolvedCount * 6, resolvedCount);
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
|
||||
int attempts = 0;
|
||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||
{
|
||||
attempts++;
|
||||
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow) || selectedRow == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!selectedPoolRowIds.Add(selectedRow.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryBuildDropItem(selectedRow, out TowerCompItemData droppedItem) || droppedItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(droppedItem);
|
||||
}
|
||||
|
||||
attempts = 0;
|
||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||
{
|
||||
attempts++;
|
||||
if (!TryRollOutGameItem(phaseIndex, themeType, out TowerCompItemData droppedItem) || droppedItem == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
candidates.Add(droppedItem);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
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;
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryBuildDropItem(selectedRow, out droppedItem);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user