refactor 2: 迁移局内资源真值

- CombatInRunResourceManager 现在持有并管理:
    - CurrentCoin
    - CurrentBaseHp
    - MaxBaseHp
    - GainedCoin / GainedGold
    - 本局 BuildTowerStats 快照
    - 奖励背包快照
    - CombatInRunResourceManager.cs:38
    - CombatInRunResourceManager.cs:46
    - CombatInRunResourceManager.cs:89
    - CombatInRunResourceManager.cs:156
- CombatScheduler 变成资源访问入口,不再通过 CombatNodeComponent 持有 coin/baseHp/build stats 真值。
    - 启动时先初始化局内资源。
    - 对外提供 CurrentCoin / CurrentGold / CurrentBaseHp / CurrentBuildTowerCount
    - 提供 TryConsumeCoin / AddCoin / TryGetBuildTowerStats
- CombatNodeComponent 已退回 facade:
    - 不再保存 _currentCoin / _currentGold / _currentBaseHp / _currentBuildTowerStats
    - 只做只读转发和启动/结束协调
- 资源变化事件现在由资源管理器发布,并补上了 delta 字段:

额外修正
- Reset() 现在会清掉奖励背包里的 ParticipantTowerInstanceIds,避免跨局残留。
- TryConsumeCoin(0) 保持原先返回 true 的语义。
This commit is contained in:
SepComet
2026-03-07 11:41:47 +08:00
parent 1d7c5b80d9
commit 853886797c
5 changed files with 207 additions and 174 deletions
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.CustomEvent;
using GeometryTD.CustomUtility;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using UnityEngine;
@@ -22,6 +24,7 @@ namespace GeometryTD.CustomComponent
private const float RarityCurveScalePhase = 30f;
private readonly List<DROutGameDropPool> _eligibleDropPoolBuffer = new();
private readonly List<TowerStatsData> _buildTowerStatsSnapshot = new();
private readonly Dictionary<RarityType, float> _rarityRollWeightBuffer = new();
private readonly BackpackInventoryData _rewardInventory = new();
@@ -30,46 +33,147 @@ namespace GeometryTD.CustomComponent
private IDataTable<DRBearingComp> _drBearingComp;
private IDataTable<DRBaseComp> _drBaseComp;
private long _nextDropItemInstanceId = 1;
private bool _isCombatActive;
public int CurrentCoin { get; private set; }
public int CurrentGold => _isCombatActive ? GainedGold : 0;
public int CurrentBaseHp { get; private set; }
public int MaxBaseHp { get; private set; }
public int CurrentBuildTowerCount => _isCombatActive ? _buildTowerStatsSnapshot.Count : 0;
public int GainedCoin { get; private set; }
public int GainedGold { get; private set; }
public void InitializeForCombat(DRLevel level)
{
Reset();
if (level == null)
{
return;
}
MaxBaseHp = Mathf.Max(0, level.BaseHp);
CurrentBaseHp = MaxBaseHp;
CurrentCoin = Mathf.Max(0, level.StartCoin);
CacheBuildTowerStatsSnapshot();
_isCombatActive = true;
}
public void MarkCombatEnded()
{
_isCombatActive = false;
}
public void Reset()
{
_isCombatActive = false;
CurrentCoin = 0;
CurrentBaseHp = 0;
MaxBaseHp = 0;
GainedCoin = 0;
GainedGold = 0;
_buildTowerStatsSnapshot.Clear();
_rewardInventory.Gold = 0;
_rewardInventory.MuzzleComponents.Clear();
_rewardInventory.BearingComponents.Clear();
_rewardInventory.BaseComponents.Clear();
_rewardInventory.Towers.Clear();
_rewardInventory.ParticipantTowerInstanceIds.Clear();
_nextDropItemInstanceId = 1;
}
public BackpackInventoryData GetRewardInventorySnapshot()
{
return new BackpackInventoryData
return InventoryCloneUtility.CloneInventory(_rewardInventory);
}
public bool TryConsumeCoin(int coin)
{
int requiredCoin = Mathf.Max(0, coin);
if (requiredCoin <= 0)
{
Gold = _rewardInventory.Gold,
MuzzleComponents = new List<MuzzleCompItemData>(_rewardInventory.MuzzleComponents),
BearingComponents = new List<BearingCompItemData>(_rewardInventory.BearingComponents),
BaseComponents = new List<BaseCompItemData>(_rewardInventory.BaseComponents),
Towers = new List<TowerItemData>(_rewardInventory.Towers)
};
return true;
}
if (!_isCombatActive || CurrentCoin < requiredCoin)
{
return false;
}
CurrentCoin -= requiredCoin;
FireCoinChangedEvent(-requiredCoin);
return true;
}
public void AddCoin(int coin)
{
int gainedCoin = Mathf.Max(0, coin);
if (!_isCombatActive || gainedCoin <= 0)
{
return;
}
CurrentCoin += gainedCoin;
FireCoinChangedEvent(gainedCoin);
}
public int ApplyBaseDamage(int damage)
{
int resolvedDamage = Mathf.Max(0, damage);
if (!_isCombatActive || resolvedDamage <= 0)
{
return CurrentBaseHp;
}
int previousBaseHp = CurrentBaseHp;
CurrentBaseHp = Mathf.Max(0, CurrentBaseHp - resolvedDamage);
int deltaBaseHp = CurrentBaseHp - previousBaseHp;
if (deltaBaseHp != 0)
{
FireBaseHpChangedEvent(deltaBaseHp);
}
return CurrentBaseHp;
}
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{
stats = null;
if (!_isCombatActive || buildIndex < 0 || buildIndex >= _buildTowerStatsSnapshot.Count)
{
return false;
}
TowerStatsData sourceStats = _buildTowerStatsSnapshot[buildIndex];
if (sourceStats == null)
{
return false;
}
stats = InventoryCloneUtility.CloneTowerStats(sourceStats);
return stats != null;
}
public void AddEnemyDefeatedReward(int gainedCoin, int gainedGold)
{
if (!_isCombatActive)
{
return;
}
int coin = Mathf.Max(0, gainedCoin);
int gold = Mathf.Max(0, gainedGold);
GainedCoin += coin;
GainedGold += gold;
if (coin > 0)
{
CurrentCoin += coin;
FireCoinChangedEvent(coin);
}
if (gold > 0)
{
_rewardInventory.Gold += gold;
}
GameEntry.CombatNode?.ApplyEnemyDropReward(coin, gold);
}
public void AddSettlementGold(int gainedGold)
@@ -84,6 +188,52 @@ namespace GeometryTD.CustomComponent
_rewardInventory.Gold += gold;
}
private void CacheBuildTowerStatsSnapshot()
{
_buildTowerStatsSnapshot.Clear();
if (GameEntry.PlayerInventory == null)
{
return;
}
IReadOnlyList<TowerItemData> towers = GameEntry.PlayerInventory.GetParticipantTowerSnapshot();
if (towers == null || towers.Count <= 0)
{
return;
}
for (int i = 0; i < towers.Count; i++)
{
TowerItemData tower = towers[i];
if (tower?.Stats == null)
{
continue;
}
_buildTowerStatsSnapshot.Add(InventoryCloneUtility.CloneTowerStats(tower.Stats));
}
}
private void FireCoinChangedEvent(int deltaCoin)
{
if (!_isCombatActive)
{
return;
}
GameEntry.Event.Fire(this, CombatCoinChangedEventArgs.Create(CurrentCoin, deltaCoin));
}
private void FireBaseHpChangedEvent(int deltaBaseHp)
{
if (!_isCombatActive)
{
return;
}
GameEntry.Event.Fire(this, CombatBaseHpChangedEventArgs.Create(CurrentBaseHp, deltaBaseHp));
}
/// <summary>
/// 击杀敌人时的掉落入口。
/// displayPhaseIndex 会影响:
@@ -48,6 +48,10 @@ namespace GeometryTD.CustomComponent
public int DisplayPhaseIndex => _phaseLoopRuntime.DisplayPhaseIndex;
public int PhaseCount => _phaseLoopRuntime.PhaseCount;
public bool CanEndCombat => _phaseLoopRuntime.CanEndCombat;
public int CurrentCoin => _combatInRunResourceManager.CurrentCoin;
public int CurrentGold => _combatInRunResourceManager.CurrentGold;
public int CurrentBaseHp => _combatInRunResourceManager.CurrentBaseHp;
public int CurrentBuildTowerCount => _combatInRunResourceManager.CurrentBuildTowerCount;
public int DefeatedEnemyCount => _enemyManager.DefeatedEnemyCount;
public int GainedCoin => _combatInRunResourceManager.GainedCoin;
public int GainedGold => _combatInRunResourceManager.GainedGold;
@@ -95,10 +99,10 @@ namespace GeometryTD.CustomComponent
_enemyManager.EndPhase();
_enemyManager.ResetCombatStats();
ResetRuntime();
_combatInRunResourceManager.Reset();
_isFinishAsVictory = true;
_currentLevel = level;
_combatInRunResourceManager.InitializeForCombat(level);
for (int i = 0; i < phases.Count; i++)
{
DRLevelPhase phase = phases[i];
@@ -215,6 +219,21 @@ namespace GeometryTD.CustomComponent
return true;
}
public bool TryConsumeCoin(int coin)
{
return _combatInRunResourceManager.TryConsumeCoin(coin);
}
public void AddCoin(int coin)
{
_combatInRunResourceManager.AddCoin(coin);
}
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{
return _combatInRunResourceManager.TryGetBuildTowerStats(buildIndex, out stats);
}
private void ResetRuntime()
{
_currentState = null;
@@ -416,7 +435,7 @@ namespace GeometryTD.CustomComponent
private void ResolveBaseHpSnapshot(out int currentBaseHp, out int maxBaseHp)
{
currentBaseHp = Mathf.Max(0, GetCurrentBaseHp());
maxBaseHp = _currentLevel != null ? Mathf.Max(0, _currentLevel.BaseHp) : 0;
maxBaseHp = _combatInRunResourceManager.MaxBaseHp;
if (maxBaseHp > 0)
{
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
@@ -609,24 +628,12 @@ namespace GeometryTD.CustomComponent
private int ApplyBaseDamage(int damage)
{
CombatNodeComponent combatNode = GameEntry.CombatNode;
if (combatNode == null)
{
return 0;
}
return combatNode.ApplyBaseDamage(damage);
return _combatInRunResourceManager.ApplyBaseDamage(damage);
}
private int GetCurrentBaseHp()
{
CombatNodeComponent combatNode = GameEntry.CombatNode;
if (combatNode == null)
{
return 0;
}
return Mathf.Max(0, combatNode.CurrentBaseHp);
return Mathf.Max(0, _combatInRunResourceManager.CurrentBaseHp);
}
private void CloseCombatFinishForm()
@@ -643,6 +650,7 @@ namespace GeometryTD.CustomComponent
{
_isCompleted = true;
_currentState = null;
_combatInRunResourceManager.MarkCombatEnded();
GameEntry.CombatNode?.OnCombatEndedByScheduler(succeeded);
}