using Games.Battles.BattleEntities;
using Games.Battles.BattleEntities.BattleUnits;
using Games.Battles.Managers.BattleUnitHandlerActionData;
using Games.Battles.Managers;
using Games.Battles.Signals;
using Sirenix.OdinInspector;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Zenject;
using Games.Battles.Calculations;
using MEC;
using System.Linq;
using Views.Games.Battles.Positioning;
using Unity.Burst;
namespace Games.Battles.PredictedCombat
{
public class PredictCombat : MonoBehaviour
{
private SignalBus _signalBus;
private DamageCalculator _damageCalculator;
private PredictCombatGrid _grid;
private Vector2Int _size = new Vector2Int(12, 8);
private List<BattleEntity> _battleEntities = new();
[Inject]
public void Construct(SignalBus signalBus, DamageCalculator damageCalculator)
{
_signalBus = signalBus;
_damageCalculator = damageCalculator;
}
public void OnCombatUnitCreate(CombatUnitCreateSignal combatUnitCreateSignal)
{
_battleEntities.Add(combatUnitCreateSignal.BattleEntity);
}
[Button(name: "Predict Combat")]
public void StartPrediction()
{
_grid = new(_size);
foreach (var entity in _battleEntities)
{
_grid.Hexes[entity.PathNavigator.HexNode.HexCoordinates].entity = entity;
}
Timing.RunCoroutine(EvaluateCombat());
}
private IEnumerator<float> EvaluateCombat()
{
BattleTeamType turn = BattleTeamType.Attacker;
Queue<BattleEntity> attackerQueue = new();
Queue<BattleEntity> deffenderQueue = new();
foreach (var entity in _battleEntities)
{
if(entity.BattleTeamType == BattleTeamType.Attacker)
{
attackerQueue.Enqueue(entity);
}
else
{
deffenderQueue.Enqueue(entity);
}
}
BattleEntity currentUnit = null;
while (!CombatEnded())
{
if(turn == BattleTeamType.Attacker)
{
foreach (var entity in attackerQueue)
{
if(entity.IsAlive)
{
currentUnit = attackerQueue.Dequeue();
attackerQueue.Enqueue(currentUnit);
}
else
{
attackerQueue.Dequeue();
}
}
}
else
{
foreach (var entity in deffenderQueue)
{
if (entity.IsAlive)
{
currentUnit = deffenderQueue.Dequeue();
deffenderQueue.Enqueue(currentUnit);
}
else
{
deffenderQueue.Dequeue();
}
}
}
var enemiesInRange = GetEnemiesInRange(currentUnit);
if(enemiesInRange.Count > 0)
{
enemiesInRange.OrderBy(x => x.statistics.Health);
HandleCombat(currentUnit, enemiesInRange[0]);
}
else
{
var walkableNodes = _grid.GetWalkableNodes(_grid.GetNodeByEntity(currentUnit), currentUnit);
var enemies = GetEnemies(currentUnit);
PredictCombatHexNode origin = _grid.GetNodeByEntity(currentUnit);
Dictionary<int, PredictCombatHexNode> nodesByDistance = new();
foreach (var node in walkableNodes)
{
int weight = 0;
foreach (var enemy in enemies)
{
int currentDistance = origin.GetDistance(_grid.GetNodeByEntity(enemy));
int distanceAfterMove = node.GetDistance(_grid.GetNodeByEntity(enemy));
weight += currentDistance - distanceAfterMove;
}
nodesByDistance.Add(weight, node);
}
var sortedNodes = new SortedDictionary<int, PredictCombatHexNode>(nodesByDistance);
_grid.MoveUnit(origin, sortedNodes.ElementAt(0).Value);
}
turn = turn == BattleTeamType.Attacker ? BattleTeamType.Defender : BattleTeamType.Attacker;
}
yield return 0;
}
private void HandleCombat(BattleEntity attacker, BattleEntity defender)
{
AttackActionData attackActionData = new(defender.PathNavigator.HexNode, attacker.PathNavigator.HexNode);
CombatContext combatContext = new(attacker, BattleUnitCombatManager.GetAttackTarget(attackActionData, attacker), attackActionData.AttackFrom, false);
var attackResult = _damageCalculator.GetAttackResult(
combatContext.Attacker,
combatContext.Target,
combatContext.BattleUnitWeapon);
defender.statistics.TakeDamage(attackResult.DamageDone);
defender.SetCount(defender.statistics.GetUnitsCount());
if (!defender.IsAlive)
{
_grid.Hexes[_grid.GetNodeByEntity(defender).hexCoordintes].entity = null;
_battleEntities.RemoveAt(_battleEntities.IndexOf(defender));
}
}
private List<BattleEntity> GetEnemies(BattleEntity origin)
{
return _battleEntities.Where(x => x.BattleTeamType != origin.BattleTeamType).ToList();
}
private List<BattleEntity> GetEnemiesInRange(BattleEntity origin)
{
var enemies = GetEnemies(origin);
List<BattleEntity> enemiesInRange = new();
PredictCombatHexNode originNode = _grid.GetNodeByEntity(origin);
foreach (var enemy in enemies)
{
if(originNode.GetDistance(_grid.GetNodeByEntity(enemy)) < origin.GetWeapon(enemy).range)
{
enemiesInRange.Add(enemy);
}
}
return enemiesInRange;
}
private bool CombatEnded()
{
if (_battleEntities.Where(x => x.BattleTeamType == BattleTeamType.Attacker && x.IsAlive).Count() < 1)
{
Debug.Log($"<color=green><b>Defender won!</b></color>");
return true;
}
else if (_battleEntities.Where(x => x.BattleTeamType == BattleTeamType.Defender && x.IsAlive).Count() < 1)
{
Debug.Log($"<color=green><b>Attacker won!</b></color>");
return true;
}
return false;
}
}
}