using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.CustomEvent;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using GeometryTD.Procedure;
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;
private bool _isCombatActive;
private bool _lastCombatSucceeded = true;
public LevelThemeType CurrentThemeType { get; private set; }
public DRLevel CurrentLevel { get; private set; }
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 => _isCombatActive ? _combatScheduler.CurrentBuildTowerCount : 0;
public int LastDefeatedEnemyCount { get; private set; }
public int LastGainedCoin { get; private set; }
public int LastGainedGold { get; private set; }
public int CurrentLevelPhaseCount
{
get
{
if (CurrentLevel == null)
{
return 0;
}
if (!_phasesByLevelId.TryGetValue(CurrentLevel.Id, out List phases) || phases == null)
{
return 0;
}
return phases.Count;
}
}
public int CurrentPhaseIndex
{
get
{
return _combatScheduler.DisplayPhaseIndex;
}
}
public void OnInit(LevelThemeType themeType)
{
ShutdownCombatRuntime();
CurrentThemeType = themeType;
CurrentLevel = null;
_isCombatActive = false;
_lastCombatSucceeded = true;
LastDefeatedEnemyCount = 0;
LastGainedCoin = 0;
LastGainedGold = 0;
_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());
EnsureCombatRuntimeInitialized();
}
public void StartCombat(
int levelId = 0,
string runId = null,
int nodeId = 0,
RunNodeType nodeType = RunNodeType.None,
int sequenceIndex = -1)
{
if (!EnsureCombatRuntimeInitialized())
{
Log.Warning("CombatNodeComponent start failed. Missing scheduler runtime.");
return;
}
bool hasLevel = levelId > 0
? TryGetLevelById(levelId, out DRLevel selectedLevel)
: TrySelectRandomLevel(out selectedLevel);
if (!hasLevel)
{
Log.Warning(
"CombatNodeComponent start failed. LevelId={0}. Missing cached level data or invalid level id.",
levelId);
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;
_isCombatActive = false;
_lastCombatSucceeded = true;
LastDefeatedEnemyCount = 0;
LastGainedCoin = 0;
LastGainedGold = 0;
if (!_combatScheduler.Start(
selectedLevel,
phaseList,
_selectedSpawnEntriesByPhaseId,
runId,
nodeId,
nodeType,
sequenceIndex))
{
CurrentLevel = null;
return;
}
_isCombatActive = true;
}
public void EndCombat()
{
if (!_isCombatActive)
{
return;
}
_isCombatActive = false;
LastDefeatedEnemyCount = _combatScheduler.DefeatedEnemyCount;
LastGainedCoin = _combatScheduler.GainedCoin;
LastGainedGold = _combatScheduler.GainedGold;
CurrentLevel = null;
}
public bool TryEndCombatByPlayer()
{
return _combatScheduler.TryEndCombatByPlayer();
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_runtimeInitialized)
{
return;
}
_combatScheduler.OnUpdate(elapseSeconds, realElapseSeconds);
}
public void OnShutdown()
{
_isCombatActive = false;
CurrentLevel = null;
_lastCombatSucceeded = true;
LastDefeatedEnemyCount = 0;
LastGainedCoin = 0;
LastGainedGold = 0;
ShutdownCombatRuntime();
}
public void OnCombatEndedByScheduler(bool succeeded)
{
_lastCombatSucceeded = succeeded;
EndCombat();
}
public bool TryConsumeCoin(int coin)
{
if (Mathf.Max(0, coin) <= 0)
{
return true;
}
if (!_isCombatActive)
{
return false;
}
return _combatScheduler.TryConsumeCoin(coin);
}
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{
if (!_isCombatActive)
{
stats = null;
return false;
}
return _combatScheduler.TryGetBuildTowerStats(buildIndex, out stats);
}
public void AddCoin(int coin)
{
if (!_isCombatActive)
{
return;
}
_combatScheduler.AddCoin(coin);
}
private void OnDestroy()
{
OnShutdown();
}
private bool EnsureCombatRuntimeInitialized()
{
if (_runtimeInitialized)
{
return true;
}
_combatScheduler.OnInit(OnCombatEndedByScheduler);
_runtimeInitialized = true;
return true;
}
private void ShutdownCombatRuntime()
{
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 bool TryGetLevelById(int levelId, out DRLevel level)
{
level = null;
if (levelId <= 0)
{
return false;
}
return _levelsById.TryGetValue(levelId, 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;
}
}
}