From 853886797cdf06d1645da9342848e7cbba6fbb54 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 7 Mar 2026 11:41:47 +0800 Subject: [PATCH] =?UTF-8?q?refactor=202:=20=E8=BF=81=E7=A7=BB=E5=B1=80?= =?UTF-8?q?=E5=86=85=E8=B5=84=E6=BA=90=E7=9C=9F=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 的语义。 --- .../CombatNode/CombatNodeComponent.cs | 157 ++-------------- .../CombatInRunResourceManager.cs | 168 +++++++++++++++++- .../CombatScheduler/CombatScheduler.cs | 40 +++-- .../Combat/CombatBaseHpChangedEventArgs.cs | 8 +- .../Combat/CombatCoinChangedEventArgs.cs | 8 +- 5 files changed, 207 insertions(+), 174 deletions(-) diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs index a600298..9ca3a80 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs @@ -23,24 +23,20 @@ namespace GeometryTD.CustomComponent private readonly Dictionary> _spawnEntriesByPhaseId = new(); private readonly Dictionary> _selectedSpawnEntriesByPhaseId = new(); private readonly List _levelIdBuffer = new(); - private readonly List _currentBuildTowerStats = new(); private readonly CombatScheduler _combatScheduler = new CombatScheduler(); private bool _runtimeInitialized; private bool _isCombatActive; - private int _currentCoin; - private int _currentGold; - private int _currentBaseHp; private bool _lastCombatSucceeded = true; public LevelThemeType CurrentThemeType { get; private set; } public DRLevel CurrentLevel { get; private set; } - public int CurrentCoin => _currentCoin; - public int CurrentGold => _currentGold; - public int CurrentBaseHp => _currentBaseHp; + public int CurrentCoin => _isCombatActive ? _combatScheduler.CurrentCoin : 0; + public int CurrentGold => _isCombatActive ? _combatScheduler.CurrentGold : 0; + public int CurrentBaseHp => _isCombatActive ? _combatScheduler.CurrentBaseHp : 0; public bool LastCombatSucceeded => _lastCombatSucceeded; public bool CanEndCombat => _combatScheduler.CanEndCombat; - public int CurrentBuildTowerCount => _currentBuildTowerStats.Count; + public int CurrentBuildTowerCount => _isCombatActive ? _combatScheduler.CurrentBuildTowerCount : 0; public int LastDefeatedEnemyCount { get; private set; } public int LastGainedCoin { get; private set; } public int LastGainedGold { get; private set; } @@ -78,9 +74,6 @@ namespace GeometryTD.CustomComponent CurrentThemeType = themeType; CurrentLevel = null; _isCombatActive = false; - _currentCoin = 0; - _currentGold = 0; - _currentBaseHp = 0; _lastCombatSucceeded = true; LastDefeatedEnemyCount = 0; LastGainedCoin = 0; @@ -90,7 +83,6 @@ namespace GeometryTD.CustomComponent _spawnEntriesByPhaseId.Clear(); _selectedSpawnEntriesByPhaseId.Clear(); _levelIdBuffer.Clear(); - _currentBuildTowerStats.Clear(); IDataTable dtLevel = GameEntry.DataTable.GetDataTable(); IDataTable dtLevelPhase = GameEntry.DataTable.GetDataTable(); @@ -184,7 +176,6 @@ namespace GeometryTD.CustomComponent public void StartCombat() { - _currentBuildTowerStats.Clear(); if (!EnsureCombatRuntimeInitialized()) { Log.Warning("CombatNodeComponent start failed. Missing scheduler runtime."); @@ -220,11 +211,7 @@ namespace GeometryTD.CustomComponent } CurrentLevel = selectedLevel; - CacheBuildTowerStatsSnapshot(); _isCombatActive = false; - _currentCoin = Mathf.Max(0, selectedLevel.StartCoin); - _currentGold = 0; - _currentBaseHp = Mathf.Max(0, selectedLevel.BaseHp); _lastCombatSucceeded = true; LastDefeatedEnemyCount = 0; LastGainedCoin = 0; @@ -232,10 +219,6 @@ namespace GeometryTD.CustomComponent if (!_combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId)) { CurrentLevel = null; - _currentCoin = 0; - _currentGold = 0; - _currentBaseHp = 0; - _currentBuildTowerStats.Clear(); return; } @@ -253,11 +236,7 @@ namespace GeometryTD.CustomComponent LastDefeatedEnemyCount = _combatScheduler.DefeatedEnemyCount; LastGainedCoin = _combatScheduler.GainedCoin; LastGainedGold = _combatScheduler.GainedGold; - _currentCoin = 0; - _currentGold = 0; - _currentBaseHp = 0; CurrentLevel = null; - _currentBuildTowerStats.Clear(); GameEntry.Event.Fire(this, NodeCompleteEventArgs.Create()); } @@ -280,144 +259,53 @@ namespace GeometryTD.CustomComponent { _isCombatActive = false; CurrentLevel = null; - _currentCoin = 0; - _currentGold = 0; - _currentBaseHp = 0; _lastCombatSucceeded = true; LastDefeatedEnemyCount = 0; LastGainedCoin = 0; LastGainedGold = 0; - _currentBuildTowerStats.Clear(); ShutdownCombatRuntime(); } - public int ApplyBaseDamage(int damage) - { - int resolvedDamage = Mathf.Max(0, damage); - if (resolvedDamage <= 0 || CurrentLevel == null || !_isCombatActive) - { - return _currentBaseHp; - } - - _currentBaseHp = Mathf.Max(0, _currentBaseHp - resolvedDamage); - FireBaseHpChangedEventIfNeeded(); - return _currentBaseHp; - } - public void OnCombatEndedByScheduler(bool succeeded) { _lastCombatSucceeded = succeeded; EndCombat(); } - public void ApplyEnemyDropReward(int coin, int gold) - { - bool coinChanged = false; - if (coin > 0) - { - _currentCoin += coin; - coinChanged = true; - } - - if (gold > 0) - { - _currentGold += gold; - } - - if (coinChanged) - { - FireCoinChangedEventIfNeeded(); - } - } - public bool TryConsumeCoin(int coin) { - int requiredCoin = Mathf.Max(0, coin); - if (requiredCoin <= 0) + if (Mathf.Max(0, coin) <= 0) { return true; } - if (_currentCoin < requiredCoin) + if (!_isCombatActive) { return false; } - _currentCoin -= requiredCoin; - FireCoinChangedEventIfNeeded(); - return true; + return _combatScheduler.TryConsumeCoin(coin); } public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats) { - stats = null; - if (buildIndex < 0 || buildIndex >= _currentBuildTowerStats.Count) + if (!_isCombatActive) { + stats = null; return false; } - stats = CloneTowerStats(_currentBuildTowerStats[buildIndex]); - return stats != null; + return _combatScheduler.TryGetBuildTowerStats(buildIndex, out stats); } public void AddCoin(int coin) { - int gainCoin = Mathf.Max(0, coin); - if (gainCoin <= 0) + if (!_isCombatActive) { return; } - _currentCoin += gainCoin; - FireCoinChangedEventIfNeeded(); - } - - - private void CacheBuildTowerStatsSnapshot() - { - _currentBuildTowerStats.Clear(); - if (GameEntry.PlayerInventory == null) - { - return; - } - - IReadOnlyList towers = GameEntry.PlayerInventory.GetParticipantTowerSnapshot(); - if (towers == null || towers.Count <= 0) - { - return; - } - - for (int i = 0; i < towers.Count; i++) - { - TowerItemData tower = towers[i]; - TowerStatsData clonedStats = CloneTowerStats(tower?.Stats); - if (clonedStats == null) - { - continue; - } - - _currentBuildTowerStats.Add(clonedStats); - } - } - - private static TowerStatsData CloneTowerStats(TowerStatsData source) - { - if (source == null) - { - return null; - } - - return new TowerStatsData - { - AttackDamage = source.AttackDamage != null ? (int[])source.AttackDamage.Clone() : null, - DamageRandomRate = source.DamageRandomRate, - RotateSpeed = source.RotateSpeed != null ? (float[])source.RotateSpeed.Clone() : null, - AttackRange = source.AttackRange != null ? (float[])source.AttackRange.Clone() : null, - AttackSpeed = source.AttackSpeed != null ? (float[])source.AttackSpeed.Clone() : null, - AttackMethodType = source.AttackMethodType, - AttackPropertyType = source.AttackPropertyType, - Tags = source.Tags != null ? (TagType[])source.Tags.Clone() : null - }; + _combatScheduler.AddCoin(coin); } private void OnDestroy() @@ -482,26 +370,5 @@ namespace GeometryTD.CustomComponent return count; } - - private void FireCoinChangedEventIfNeeded() - { - if (!_isCombatActive) - { - return; - } - - GameEntry.Event.Fire(this, CombatCoinChangedEventArgs.Create(_currentCoin)); - } - - private void FireBaseHpChangedEventIfNeeded() - { - if (!_isCombatActive) - { - return; - } - - GameEntry.Event.Fire(this, CombatBaseHpChangedEventArgs.Create(_currentBaseHp)); - } } } - diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs index f11fd8c..afb22e0 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs @@ -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 _eligibleDropPoolBuffer = new(); + private readonly List _buildTowerStatsSnapshot = new(); private readonly Dictionary _rarityRollWeightBuffer = new(); private readonly BackpackInventoryData _rewardInventory = new(); @@ -30,46 +33,147 @@ namespace GeometryTD.CustomComponent private IDataTable _drBearingComp; private IDataTable _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(_rewardInventory.MuzzleComponents), - BearingComponents = new List(_rewardInventory.BearingComponents), - BaseComponents = new List(_rewardInventory.BaseComponents), - Towers = new List(_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 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)); + } + /// /// 击杀敌人时的掉落入口。 /// displayPhaseIndex 会影响: diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs index 4cca942..f1e2e7e 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs @@ -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); } diff --git a/Assets/GameMain/Scripts/Event/Combat/CombatBaseHpChangedEventArgs.cs b/Assets/GameMain/Scripts/Event/Combat/CombatBaseHpChangedEventArgs.cs index a683b75..7486e02 100644 --- a/Assets/GameMain/Scripts/Event/Combat/CombatBaseHpChangedEventArgs.cs +++ b/Assets/GameMain/Scripts/Event/Combat/CombatBaseHpChangedEventArgs.cs @@ -10,17 +10,20 @@ namespace GeometryTD.CustomEvent public override int Id => EventId; public int CurrentBaseHp { get; private set; } + public int DeltaBaseHp { get; private set; } public CombatBaseHpChangedEventArgs() { CurrentBaseHp = 100; + DeltaBaseHp = 0; } - public static CombatBaseHpChangedEventArgs Create(int currentBaseHp) + public static CombatBaseHpChangedEventArgs Create(int currentBaseHp, int deltaBaseHp = 0) { var args = ReferencePool.Acquire(); args.CurrentBaseHp = currentBaseHp; + args.DeltaBaseHp = deltaBaseHp; return args; } @@ -28,6 +31,7 @@ namespace GeometryTD.CustomEvent public override void Clear() { CurrentBaseHp = 100; + DeltaBaseHp = 0; } } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/Event/Combat/CombatCoinChangedEventArgs.cs b/Assets/GameMain/Scripts/Event/Combat/CombatCoinChangedEventArgs.cs index 35f9cb3..f0b173c 100644 --- a/Assets/GameMain/Scripts/Event/Combat/CombatCoinChangedEventArgs.cs +++ b/Assets/GameMain/Scripts/Event/Combat/CombatCoinChangedEventArgs.cs @@ -10,17 +10,20 @@ namespace GeometryTD.CustomEvent public override int Id => EventId; public int CurrentCoin { get; private set; } + public int DeltaCoin { get; private set; } public CombatCoinChangedEventArgs() { CurrentCoin = 0; + DeltaCoin = 0; } - public static CombatCoinChangedEventArgs Create(int currentCoin) + public static CombatCoinChangedEventArgs Create(int currentCoin, int deltaCoin = 0) { var args = ReferencePool.Acquire(); args.CurrentCoin = currentCoin; + args.DeltaCoin = deltaCoin; return args; } @@ -28,6 +31,7 @@ namespace GeometryTD.CustomEvent public override void Clear() { CurrentCoin = 0; + DeltaCoin = 0; } } -} \ No newline at end of file +}