From ca7b2f2dca53d922627c7e4d729403c451fd8afb Mon Sep 17 00:00:00 2001 From: SepComet <2428390463@qq.com> Date: Sat, 7 Mar 2026 15:12:18 +0800 Subject: [PATCH] =?UTF-8?q?refactor=205:=20-=20MapData.cs=20=E7=8E=B0?= =?UTF-8?q?=E5=9C=A8=E4=BC=9A=E6=90=BA=E5=B8=A6=E6=88=98=E6=96=97=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E4=B8=8A=E4=B8=8B=E6=96=87=EF=BC=9A=20=20=20=20=20-?= =?UTF-8?q?=20InitialCoin=20=20=20=20=20-=20=E5=BB=BA=E5=A1=94=20TowerStat?= =?UTF-8?q?sData=20=E5=BF=AB=E7=85=A7=20=20=20=20=20-=20TryConsumeCoin=20/?= =?UTF-8?q?=20AddCoin=20=E8=BF=90=E8=A1=8C=E6=97=B6=E5=9B=9E=E8=B0=83=20-?= =?UTF-8?q?=20CombatLoadingState.cs=20=E8=B4=9F=E8=B4=A3=E4=BB=8E=20Combat?= =?UTF-8?q?InRunResourceManager=20=E8=AF=BB=E5=8F=96=20coin=20=E5=92=8C=20?= =?UTF-8?q?build=20stats=20=E5=BF=AB=E7=85=A7=EF=BC=8C=E7=BB=84=E8=A3=85?= =?UTF-8?q?=20MapData=20=E5=90=8E=E5=86=8D=E4=BA=A4=E7=BB=99=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=93=BE=E3=80=82=20-=20CombatLoadSession.cs=20?= =?UTF-8?q?=E6=94=B9=E6=88=90=E6=8E=A5=E6=94=B6=E5=A4=96=E9=83=A8=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=A5=BD=E7=9A=84=20MapData=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=87=AA=E5=B7=B1=E5=8F=AA=E5=A1=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=20LevelId=E3=80=82=20-=20CombatScheduler.cs=20=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=9C=A8=20Start()=20=E9=87=8C=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=8F=91=E8=B5=B7=E5=9C=B0=E5=9B=BE=E5=8A=A0=E8=BD=BD=EF=BC=8C?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=BB=86=E8=8A=82=E5=9B=9E=E5=88=B0=20Combat?= =?UTF-8?q?LoadingState=E3=80=82=20-=20MapEntity.cs=20=E5=B7=B2=E7=BB=8F?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E7=9B=B4=E6=8E=A5=E8=AF=BB=20GameEntry.Comba?= =?UTF-8?q?tNode=20=E7=9A=84=20coin=20/=20build=20count=20/=20build=20stat?= =?UTF-8?q?s=EF=BC=8C=E4=B9=9F=E4=B8=8D=E5=86=8D=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E5=AE=83=E5=81=9A=20coin=20=E8=AF=BB=E5=86=99=EF=BC=9A=20=20?= =?UTF-8?q?=20=20=20-=20=E5=88=9D=E5=A7=8B=20coin=20=E5=92=8C=20build=20st?= =?UTF-8?q?ats=20=E4=BB=8E=20MapData=20=E8=AF=BB=E5=8F=96=20=20=20=20=20-?= =?UTF-8?q?=20=E5=90=8E=E7=BB=AD=20coin=20=E9=80=9A=E8=BF=87=20CombatCoinC?= =?UTF-8?q?hangedEventArgs=20=E5=90=8C=E6=AD=A5=20=20=20=20=20-=20?= =?UTF-8?q?=E5=BB=BA=E5=A1=94/=E5=8D=87=E7=BA=A7/=E6=8B=86=E5=A1=94?= =?UTF-8?q?=E6=97=B6=E7=9A=84=20coin=20=E5=8F=98=E6=9B=B4=E9=80=9A?= =?UTF-8?q?=E8=BF=87=20MapData=20=E6=B3=A8=E5=85=A5=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombatScheduler/CombatLoadSession.cs | 13 ++- .../CombatScheduler/CombatScheduler.cs | 5 - .../CombatStates/CombatLoadingState.cs | 43 +++++++ .../Scripts/Entity/EntityData/MapData.cs | 108 ++++++++++++++++++ .../Scripts/Entity/EntityLogic/MapEntity.cs | 66 +++++++++-- 5 files changed, 213 insertions(+), 22 deletions(-) diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs index 1ffba60..cdf2579 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs @@ -45,7 +45,7 @@ namespace GeometryTD.CustomComponent _currentMap = null; } - public bool StartLoading(DRLevel level, out string errorMessage) + public bool StartLoading(DRLevel level, MapData mapData, out string errorMessage) { errorMessage = null; if (_entity == null) @@ -54,7 +54,7 @@ namespace GeometryTD.CustomComponent return false; } - if (!TryShowMap(level, out errorMessage)) + if (!TryShowMap(level, mapData, out errorMessage)) { return false; } @@ -224,7 +224,7 @@ namespace GeometryTD.CustomComponent } } - private bool TryShowMap(DRLevel level, out string errorMessage) + private bool TryShowMap(DRLevel level, MapData mapData, out string errorMessage) { errorMessage = null; if (level == null) @@ -242,7 +242,10 @@ namespace GeometryTD.CustomComponent } _loadingMapEntityId = _entity.GenerateSerialId(); - _entity.ShowMap(new MapData(_loadingMapEntityId, level.Id, Vector3.zero), mapAssetName); + MapData resolvedMapData = mapData != null + ? mapData.CloneForEntity(_loadingMapEntityId, Vector3.zero) + : new MapData(_loadingMapEntityId, level.Id, Vector3.zero); + _entity.ShowMap(resolvedMapData, mapAssetName); return true; } @@ -274,4 +277,4 @@ namespace GeometryTD.CustomComponent _isCombatInfoFormReady = false; } } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs index f746412..586237b 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs @@ -120,11 +120,6 @@ namespace GeometryTD.CustomComponent return HandleStartFailure($"CombatScheduler start failed. Level '{level.Id}' has no phase data."); } - if (!_loadSession.StartLoading(level, out string loadError)) - { - return HandleStartFailure($"CombatScheduler start failed. {loadError}"); - } - ChangeState(new CombatLoadingState(this)); Log.Info( "CombatScheduler started. Level={0}, PhaseCount={1}.", diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/CombatLoadingState.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/CombatLoadingState.cs index e6a6739..4567c92 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/CombatLoadingState.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/CombatLoadingState.cs @@ -1,3 +1,8 @@ +using System.Collections.Generic; +using GeometryTD.Definition; +using GeometryTD.Entity.EntityData; +using UnityEngine; + namespace GeometryTD.CustomComponent { public partial class CombatScheduler @@ -8,6 +13,21 @@ namespace GeometryTD.CustomComponent { } + public override void OnEnter() + { + if (Scheduler._currentLevel == null) + { + Scheduler.EnterFailureFallback("Combat loading failed. Current level is null."); + return; + } + + MapData mapData = BuildMapData(); + if (!Scheduler._loadSession.StartLoading(Scheduler._currentLevel, mapData, out string errorMessage)) + { + Scheduler.EnterFailureFallback($"Combat loading failed. {errorMessage}"); + } + } + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) { _ = elapseSeconds; @@ -20,6 +40,29 @@ namespace GeometryTD.CustomComponent Scheduler.TryBeginNextPhase(); } + + private MapData BuildMapData() + { + List buildTowerStatsSnapshot = new(); + for (int i = 0; i < Scheduler._combatInRunResourceManager.CurrentBuildTowerCount; i++) + { + if (Scheduler._combatInRunResourceManager.TryGetBuildTowerStats(i, out TowerStatsData stats) && + stats != null) + { + buildTowerStatsSnapshot.Add(stats); + } + } + + return new MapData( + entityId: 0, + typeId: 0, + levelId: Scheduler._currentLevel.Id, + position: Vector3.zero, + initialCoin: Scheduler._combatInRunResourceManager.CurrentCoin, + buildTowerStatsSnapshot: buildTowerStatsSnapshot, + tryConsumeCoin: Scheduler.TryConsumeCoin, + addCoin: Scheduler.AddCoin); + } } } } diff --git a/Assets/GameMain/Scripts/Entity/EntityData/MapData.cs b/Assets/GameMain/Scripts/Entity/EntityData/MapData.cs index e5e05f3..977c164 100644 --- a/Assets/GameMain/Scripts/Entity/EntityData/MapData.cs +++ b/Assets/GameMain/Scripts/Entity/EntityData/MapData.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using GeometryTD.Definition; +using GeometryTD.CustomUtility; using UnityEngine; namespace GeometryTD.Entity.EntityData @@ -7,6 +10,11 @@ namespace GeometryTD.Entity.EntityData public class MapData : EntityDataBase { [SerializeField] private int _levelId = 0; + [SerializeField] private int _initialCoin = 0; + [SerializeField] private TowerStatsData[] _buildTowerStatsSnapshot = Array.Empty(); + + [NonSerialized] private Func _tryConsumeCoin; + [NonSerialized] private Action _addCoin; public MapData(int entityId, int levelId, Vector3 position) : this(entityId, 0, levelId, position) { @@ -18,10 +26,110 @@ namespace GeometryTD.Entity.EntityData Position = position; } + public MapData( + int entityId, + int typeId, + int levelId, + Vector3 position, + int initialCoin, + IReadOnlyList buildTowerStatsSnapshot, + Func tryConsumeCoin, + Action addCoin) : base(entityId, typeId) + { + _levelId = levelId; + Position = position; + _initialCoin = Mathf.Max(0, initialCoin); + SetBuildTowerStatsSnapshot(buildTowerStatsSnapshot); + _tryConsumeCoin = tryConsumeCoin; + _addCoin = addCoin; + } + public int LevelId { get => _levelId; set => _levelId = value; } + + public int InitialCoin + { + get => _initialCoin; + set => _initialCoin = Mathf.Max(0, value); + } + + public int CurrentBuildTowerCount => _buildTowerStatsSnapshot != null ? _buildTowerStatsSnapshot.Length : 0; + + public void BindCombatCallbacks(Func tryConsumeCoin, Action addCoin) + { + _tryConsumeCoin = tryConsumeCoin; + _addCoin = addCoin; + } + + public bool TryConsumeCoin(int coin) + { + int requiredCoin = Mathf.Max(0, coin); + if (requiredCoin <= 0) + { + return true; + } + + return _tryConsumeCoin != null && _tryConsumeCoin.Invoke(requiredCoin); + } + + public void AddCoin(int coin) + { + int amount = Mathf.Max(0, coin); + if (amount <= 0) + { + return; + } + + _addCoin?.Invoke(amount); + } + + public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats) + { + stats = null; + if (_buildTowerStatsSnapshot == null || buildIndex < 0 || buildIndex >= _buildTowerStatsSnapshot.Length) + { + return false; + } + + TowerStatsData sourceStats = _buildTowerStatsSnapshot[buildIndex]; + if (sourceStats == null) + { + return false; + } + + stats = InventoryCloneUtility.CloneTowerStats(sourceStats); + return stats != null; + } + + public void SetBuildTowerStatsSnapshot(IReadOnlyList buildTowerStatsSnapshot) + { + if (buildTowerStatsSnapshot == null || buildTowerStatsSnapshot.Count <= 0) + { + _buildTowerStatsSnapshot = Array.Empty(); + return; + } + + _buildTowerStatsSnapshot = new TowerStatsData[buildTowerStatsSnapshot.Count]; + for (int i = 0; i < buildTowerStatsSnapshot.Count; i++) + { + _buildTowerStatsSnapshot[i] = InventoryCloneUtility.CloneTowerStats(buildTowerStatsSnapshot[i]); + } + } + + public MapData CloneForEntity(int entityId, Vector3 position) + { + return new MapData( + entityId, + TypeId, + _levelId, + position, + _initialCoin, + _buildTowerStatsSnapshot, + _tryConsumeCoin, + _addCoin); + } } } diff --git a/Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs b/Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs index 5778ebb..3c96445 100644 --- a/Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs +++ b/Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using GeometryTD.CustomComponent; +using GeometryTD.CustomEvent; +using GameFramework.Event; using GeometryTD.Map; using GeometryTD.Definition; using GeometryTD.Entity.EntityData; @@ -37,6 +39,8 @@ namespace GeometryTD.Entity private TowerSelectionPresenter _towerSelectionPresenter; private CombatSelectUseCaseConfigurator _combatSelectUseCaseConfigurator; + private int _currentCoin; + private bool _isCoinEventSubscribed; public IReadOnlyList PathCells => _mapTopologyService != null ? _mapTopologyService.PathCells @@ -117,6 +121,11 @@ namespace GeometryTD.Entity { Log.Warning("MapData is invalid for map entity '{0}'.", Id); } + else + { + _currentCoin = Mathf.Max(0, _mapData.InitialCoin); + SubscribeCombatEvents(); + } RefreshTiles(); ConfigureCombatSelectUseCase(); @@ -125,6 +134,7 @@ namespace GeometryTD.Entity protected override void OnHide(bool isShutdown, object userData) { + UnsubscribeCombatEvents(); HideCombatSelectForm(); _towerPlacementService?.HideAndClearAllPlacedTowers(); ClearSelectionState(); @@ -301,8 +311,7 @@ namespace GeometryTD.Entity return false; } - CombatNodeComponent combatNode = GameEntry.CombatNode; - if (combatNode == null || !combatNode.TryGetBuildTowerStats(buildIndex, out TowerStatsData buildTowerStats)) + if (_mapData == null || !_mapData.TryGetBuildTowerStats(buildIndex, out TowerStatsData buildTowerStats)) { return false; } @@ -376,17 +385,49 @@ namespace GeometryTD.Entity GameEntry.UIRouter.CloseUI(UIFormType.CombatSelectForm); } - private static int GetCurrentBuildTowerCount() + private void SubscribeCombatEvents() { - if (GameEntry.CombatNode == null) + if (_isCoinEventSubscribed) + { + return; + } + + GameEntry.Event.Subscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged); + _isCoinEventSubscribed = true; + } + + private void UnsubscribeCombatEvents() + { + if (!_isCoinEventSubscribed) + { + return; + } + + GameEntry.Event.Unsubscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged); + _isCoinEventSubscribed = false; + } + + private void OnCombatCoinChanged(object sender, GameEventArgs e) + { + if (e is not CombatCoinChangedEventArgs args) + { + return; + } + + _currentCoin = Mathf.Max(0, args.CurrentCoin); + } + + private int GetCurrentBuildTowerCount() + { + if (_mapData == null) { return 0; } - return Mathf.Clamp(GameEntry.CombatNode.CurrentBuildTowerCount, 0, 4); + return Mathf.Clamp(_mapData.CurrentBuildTowerCount, 0, 4); } - private static bool TryConsumeCoin(int cost) + private bool TryConsumeCoin(int cost) { int requiredCoin = Mathf.Max(0, cost); if (requiredCoin <= 0) @@ -394,27 +435,28 @@ namespace GeometryTD.Entity return true; } - if (GameEntry.CombatNode == null) + if (_mapData == null) { return false; } - return GameEntry.CombatNode.TryConsumeCoin(requiredCoin); + return _mapData.TryConsumeCoin(requiredCoin); } - private static int GetCurrentCoin() + private int GetCurrentCoin() { - return GameEntry.CombatNode != null ? Mathf.Max(0, GameEntry.CombatNode.CurrentCoin) : 0; + return Mathf.Max(0, _currentCoin); } - private static void AddCoin(int coin) + private void AddCoin(int coin) { int amount = Mathf.Max(0, coin); if (amount <= 0) { return; } - GameEntry.CombatNode?.AddCoin(amount); + + _mapData?.AddCoin(amount); } } }