using System.Collections.Generic; using GameFramework.DataTable; using GeometryTD.CustomEvent; using GeometryTD.DataTable; using GeometryTD.Definition; using UnityEngine; using UnityGameFramework.Runtime; namespace GeometryTD.CustomComponent { /// /// 鎴樻枟鑺傜偣缁勪欢 /// public class CombatNodeComponent : GameFrameworkComponent { // Level.Id => Level private readonly Dictionary _levelsById = new(); // LevelId => LevelPhases private readonly Dictionary> _phasesByLevelId = new(); // LevelPhase.Id => LevelSpawnEntries private readonly Dictionary> _spawnEntriesByPhaseId = new(); private readonly Dictionary> _selectedSpawnEntriesByPhaseId = new(); private readonly List _levelIdBuffer = new(); private readonly CombatScheduler _combatScheduler = new CombatScheduler(); private bool _runtimeInitialized; public LevelThemeType CurrentThemeType { get; private set; } public DRLevel CurrentLevel { get; private set; } public void OnInit(LevelThemeType themeType) { ShutdownBattleRuntime(); CurrentThemeType = themeType; CurrentLevel = null; _levelsById.Clear(); _phasesByLevelId.Clear(); _spawnEntriesByPhaseId.Clear(); _selectedSpawnEntriesByPhaseId.Clear(); _levelIdBuffer.Clear(); IDataTable dtLevel = GameEntry.DataTable.GetDataTable(); IDataTable dtLevelPhase = GameEntry.DataTable.GetDataTable(); IDataTable dtSpawnEntry = GameEntry.DataTable.GetDataTable(); if (dtLevel == null || dtLevelPhase == null || dtSpawnEntry == null) { Log.Warning("CombatNodeComponent init failed. Missing data table(s)."); return; } DRLevel[] levels = dtLevel.GetAllDataRows(); for (int i = 0; i < levels.Length; i++) { DRLevel level = levels[i]; if (level.LevelThemeType != themeType) { continue; } _levelsById[level.Id] = level; _phasesByLevelId[level.Id] = new List(); _levelIdBuffer.Add(level.Id); } DRLevelPhase[] levelPhases = dtLevelPhase.GetAllDataRows(); foreach (var phase in levelPhases) { int levelId = phase.Id / 1000; if (!_levelsById.ContainsKey(levelId)) { continue; } if (!_phasesByLevelId.TryGetValue(levelId, out List phases)) { phases = new List(); _phasesByLevelId[levelId] = phases; } phases.Add(phase); _spawnEntriesByPhaseId[phase.Id] = new List(); } DRLevelSpawnEntry[] spawnEntries = dtSpawnEntry.GetAllDataRows(); foreach (var spawnEntry in spawnEntries) { int phaseId = spawnEntry.Id / 1000; int levelId = phaseId / 1000; if (!_levelsById.ContainsKey(levelId)) { continue; } if (!_spawnEntriesByPhaseId.TryGetValue(phaseId, out List entries)) { entries = new List(); _spawnEntriesByPhaseId[phaseId] = entries; } entries.Add(spawnEntry); } foreach (List phaseList in _phasesByLevelId.Values) { phaseList.Sort((left, right) => left.Id.CompareTo(right.Id)); } foreach (List phaseSpawnEntries in _spawnEntriesByPhaseId.Values) { phaseSpawnEntries.Sort((left, right) => { int timeCompare = left.StartTime.CompareTo(right.StartTime); if (timeCompare != 0) { return timeCompare; } return left.Id.CompareTo(right.Id); }); } Log.Info( "CombatNodeComponent initialized. Theme={0}, Levels={1}, Phases={2}, Entries={3}.", themeType, _levelsById.Count, CountPhases(), CountEntries()); EnsureBattleRuntimeInitialized(); } public void StartCombat() { if (!EnsureBattleRuntimeInitialized()) { Log.Warning("CombatNodeComponent start failed. Missing scheduler runtime."); return; } if (!TrySelectRandomLevel(out DRLevel selectedLevel)) { Log.Warning("CombatNodeComponent has no level cache. Call OnInit(levelThemeType) first."); return; } if (!_phasesByLevelId.TryGetValue(selectedLevel.Id, out List phaseList) || phaseList == null || phaseList.Count <= 0) { Log.Warning("CombatNodeComponent start failed. Level '{0}' has no phase data.", selectedLevel.Id); return; } _selectedSpawnEntriesByPhaseId.Clear(); foreach (var phase in phaseList) { if (_spawnEntriesByPhaseId.TryGetValue(phase.Id, out List entries) && entries != null) { _selectedSpawnEntriesByPhaseId[phase.Id] = entries; } else { _selectedSpawnEntriesByPhaseId[phase.Id] = new List(); } } CurrentLevel = selectedLevel; _combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId); GameEntry.Event.Fire(this, NodeEnterEventArgs.Create()); } public void EndCombat() { GameEntry.Event.Fire(this, NodeCompleteEventArgs.Create()); } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { if (!_runtimeInitialized) { return; } _combatScheduler.OnUpdate(elapseSeconds, realElapseSeconds); } public void OnShutdown() { CurrentLevel = null; ShutdownBattleRuntime(); } private void OnDestroy() { OnShutdown(); } private bool EnsureBattleRuntimeInitialized() { if (_runtimeInitialized) { return true; } _combatScheduler.OnInit(); _runtimeInitialized = true; return true; } private void ShutdownBattleRuntime() { if (!_runtimeInitialized) { return; } _combatScheduler.OnDestroy(); _runtimeInitialized = false; } private bool TrySelectRandomLevel(out DRLevel level) { level = null; if (_levelIdBuffer.Count <= 0) { return false; } int selectedIndex = Random.Range(0, _levelIdBuffer.Count); int selectedLevelId = _levelIdBuffer[selectedIndex]; return _levelsById.TryGetValue(selectedLevelId, out level); } private int CountPhases() { int count = 0; foreach (List phases in _phasesByLevelId.Values) { count += phases.Count; } return count; } private int CountEntries() { int count = 0; foreach (List list in _spawnEntriesByPhaseId.Values) { count += list.Count; } return count; } } }