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;
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 bool LastCombatSucceeded => _lastCombatSucceeded;
public bool CanEndCombat => _combatScheduler.CanEndCombat;
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;
_currentCoin = 0;
_currentGold = 0;
_currentBaseHp = 0;
_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()
{
if (!EnsureCombatRuntimeInitialized())
{
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;
_isCombatActive = false;
_currentCoin = Mathf.Max(0, selectedLevel.StartCoin);
_currentGold = 0;
_currentBaseHp = Mathf.Max(0, selectedLevel.BaseHp);
_lastCombatSucceeded = true;
LastDefeatedEnemyCount = 0;
LastGainedCoin = 0;
LastGainedGold = 0;
if (!_combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId))
{
CurrentLevel = null;
_currentCoin = 0;
_currentGold = 0;
_currentBaseHp = 0;
return;
}
_isCombatActive = true;
GameEntry.Event.Fire(this, NodeEnterEventArgs.Create());
}
public void EndCombat()
{
if (!_isCombatActive)
{
return;
}
_isCombatActive = false;
LastDefeatedEnemyCount = _combatScheduler.DefeatedEnemyCount;
LastGainedCoin = _combatScheduler.GainedCoin;
LastGainedGold = _combatScheduler.GainedGold;
_currentCoin = 0;
_currentGold = 0;
_currentBaseHp = 0;
CurrentLevel = null;
GameEntry.Event.Fire(this, NodeCompleteEventArgs.Create());
}
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;
_currentCoin = 0;
_currentGold = 0;
_currentBaseHp = 0;
_lastCombatSucceeded = true;
LastDefeatedEnemyCount = 0;
LastGainedCoin = 0;
LastGainedGold = 0;
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)
{
return true;
}
if (_currentCoin < requiredCoin)
{
return false;
}
_currentCoin -= requiredCoin;
FireCoinChangedEventIfNeeded();
return true;
}
public void AddCoin(int coin)
{
int gainCoin = Mathf.Max(0, coin);
if (gainCoin <= 0)
{
return;
}
_currentCoin += gainCoin;
FireCoinChangedEventIfNeeded();
}
private void OnDestroy()
{
OnShutdown();
}
private bool EnsureCombatRuntimeInitialized()
{
if (_runtimeInitialized)
{
return true;
}
_combatScheduler.OnInit();
_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 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;
}
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));
}
}
}