using System; using System.Collections.Generic; using System.IO; using System.Linq; using AI; using Chars; using Data; using DefaultNamespace; using DG.Tweening; using HexFiled; using Items; using UnityEngine; using Weapons; using Object = UnityEngine.Object; namespace Units { public class Unit { private bool _isAlive; private GameObject _instance; private List _inventory; private AnimLength _animLength; private HexCell _cell; private HexGrid _hexGrid; public event Action OnPlayerSpawned; private Animator _animator; private UnitView _unitView; private bool _isBusy; private UnitInfo _data; private int _hp; private int _mana; private Weapon _weapon; private Vector2 _direction; private BarCanvas _barCanvas; private bool _isHardToCapture; private bool _isCapturing; private int _defenceBonus; private Camera _camera; public int AttackBonus => _weapon.modifiedDamage - _weapon.damage; public int DefenceBonus => _defenceBonus; public bool IsBusy { get => _isBusy; set => _isBusy = value; } public UnitView UnitView => _unitView; public bool IsAlive => _isAlive; public UnitColor Color => _data.color; public int InventoryCapacity => _data.inventoryCapacity; public event Action OnItemPickUp; public event Action OnDeath; public BarCanvas BarCanvas => _barCanvas; public GameObject Instance => _instance; public UnitInfo Data => _data; public int Mana => _mana; public int Hp => _hp; public List Inventory => _inventory; public Weapon Weapon => _weapon; public Animator Animator => _animator; public Unit(UnitInfo unitData, Weapon weapon, HexGrid hexGrid) { _camera = Camera.main; _weapon = weapon; _data = unitData; _isAlive = false; _hexGrid = hexGrid; _isBusy = false; _isHardToCapture = false; _isCapturing = false; } public void SetUpBonus(float duration, int value, BonusType type) { switch (type) { case BonusType.Attack: TimerHelper.Instance.StartTimer(() => _weapon.SetModifiedDamage(0), duration); _weapon.SetModifiedDamage(value); break; case BonusType.Defence: TimerHelper.Instance.StartTimer(() => _defenceBonus = 0, duration); _defenceBonus = value; break; case BonusType.Heal: break; default: break; } } public void Move(HexDirection direction) { if (_cell.GetNeighbor(direction) == null || _isBusy || (_cell.GetNeighbor(direction).Color != Color && HexManager.UnitCurrentCell.TryGetValue(_cell.GetNeighbor(direction).Color, out var value) && value.cell.coordinates.Equals(_cell.GetNeighbor(direction).coordinates))) return; _unitView.StopHardCapture(); if (_cell.GetNeighbor(direction).Color == _data.color) { DoTransit(direction); } else if (_cell.GetNeighbor(direction).Color != UnitColor.GREY) { if (_mana - _hexGrid.HexHardCaptureCost <= 0) return; _isHardToCapture = true; DoTransit(direction); } else if (_mana - _hexGrid.HexCaptureCost >= 0) { if (_mana - _hexGrid.HexHardCaptureCost <= 0) return; DoTransit(direction); } } private void DoTransit(HexDirection direction) { _isBusy = true; _isCapturing = _data.color != _cell.GetNeighbor(direction).Color; _cell = _cell.GetNeighbor(direction); HexManager.UnitCurrentCell[_data.color] = (_cell, this); RotateUnit(new Vector2((_cell.transform.position - _instance.transform.position).normalized.x, (_cell.transform.position - _instance.transform.position).normalized.z)); _animator.SetTrigger("Move"); _animator.SetBool("isMoving", _isBusy); _instance.transform.DOMove(_cell.transform.position, _animLength.Move); } private void CaptureHex() { if (_isHardToCapture) { _mana -= _hexGrid.HexHardCaptureCost; } else { _mana -= _hexGrid.HexCaptureCost; } UnitView.RegenMana(); UpdateBarCanvas(); _isBusy = false; _isHardToCapture = false; _cell.PaintHex(_data.color); } private void SetAnimLength() { AnimationClip[] clips = _animator.runtimeAnimatorController.animationClips; foreach (var clip in clips) { switch (clip.name) { case "MoveJump": _animLength.Move = clip.length; break; case "Attack": _animLength.Attack = clip.length; break; case "Dead": _animLength.Death = clip.length; break; default: break; } } } public void Spawn(HexCoordinates hexCoordinates) { if (!_isAlive) { _cell = _hexGrid.GetCellFromCoord(hexCoordinates); _cell.PaintHex(_data.color); _cell.GetListNeighbours().ForEach(x => x?.PaintHex(Color)); _inventory = new List(); HexManager.UnitCurrentCell.Add(_data.color, (_cell, this)); _instance = Object.Instantiate(_data.unitPrefa, _cell.transform.parent); _instance.transform.localPosition = _cell.transform.localPosition; _isAlive = true; _animator = _instance.GetComponent(); _unitView = _instance.GetComponent(); _barCanvas = _unitView.BarCanvas; _unitView.SetUp(_barCanvas.SpawnShotUI(_weapon.shots), _weapon, RegenMana, _data.manaRegen, CaptureHex, this, _hexGrid.HardCaptureTime); SetAnimLength(); MusicController.Instance.AddAudioSource(_instance); _mana = _data.maxMana; _hp = _data.maxHP; SetUpActions(); _weapon.SetModifiedDamage(0); BarCanvas.transform.LookAt( BarCanvas.transform.position + _camera.transform.rotation * Vector3.back, _camera.transform.rotation * Vector3.up); _isBusy = false; OnPlayerSpawned?.Invoke(_instance); } } private void RegenMana() { _mana += _data.manaRegen; UpdateBarCanvas(); } public bool PickUpItem(Item item) { if (_inventory.Count < _data.inventoryCapacity) { item.PickUp(_data.color); _inventory.Add(item); OnItemPickUp?.Invoke(item); return true; } return false; } public void UseItem(Item item) { _inventory.Remove(item); } private void MoveEnd() { _isBusy = false; _animator.SetBool("isMoving", _isBusy); if (!_isCapturing) { return; } if (_isHardToCapture) { _unitView.HardCaptureHex(_cell); } else { CaptureHex(); } _isHardToCapture = false; } private void AttackEnd() { _isBusy = false; UpdateBarCanvas(); } private void Attacking() { Aim(_direction); _weapon.Fire(_instance.transform, _direction); } private void SetUpActions() { _unitView.OnStep += MoveEnd; _unitView.OnAttackEnd += AttackEnd; _unitView.OnAttack += Attacking; _unitView.OnHit += Damage; } private void UpdateBarCanvas() { if (_hp > _data.maxHP) _hp = _data.maxHP; if (_mana > _data.maxMana) _mana = _data.maxMana; float hp = _hp; float mana = _mana; float maxHp = _data.maxHP; float maxMana = _data.maxMana; _barCanvas.ManaBar.DOFillAmount(mana / maxMana, 0.5f).SetEase(Ease.InQuad); _barCanvas.HealthBar.DOFillAmount(hp / maxHp, 0.5f).SetEase(Ease.InQuad); } public void Death() { _unitView.OnStep -= MoveEnd; _unitView.OnAttackEnd -= AttackEnd; _unitView.OnAttack -= Attacking; _unitView.OnHit -= Damage; _isAlive = false; _isBusy = true; HexManager.UnitCurrentCell.Remove(Color); _animator.SetTrigger("Death"); var vfx = VFXController.Instance.PlayEffect(HexGrid.Colors[Color].VFXDeathPrefab, _instance.transform.position); TimerHelper.Instance.StartTimer(() => { Object.Destroy(_instance); OnDeath?.Invoke(this); }, _animLength.Death); _inventory.ForEach(x => x.Dispose()); MusicController.Instance.AddAudioSource(vfx); MusicController.Instance.PlayAudioClip(MusicController.Instance.MusicData.SfxMusic.Death, vfx); MusicController.Instance.RemoveAudioSource(_instance); } public void StartAttack() { if (_isBusy || !_unitView.Shoot()) return; _isBusy = true; if (_direction.Equals(Vector2.zero)) { var enemy = AIManager.GetNearestUnit(_weapon.disnatce, this); if (enemy == null) _direction = new Vector2(_unitView.transform.forward.x, _unitView.transform.forward.z); else { var dir = DirectionHelper.DirectionTo(_instance.transform.position, enemy.Instance.transform.position); _direction = new Vector2(dir.x, dir.z); } } RotateUnit(_direction); _animator.SetTrigger("Attack"); } public void RotateUnit(Vector2 direction) { _unitView.transform.DOLookAt(new Vector3(direction.x, 0, direction.y) + _unitView.transform.position, 0.1f).onUpdate += () => BarCanvas.transform.LookAt( BarCanvas.transform.position + _camera.transform.rotation * Vector3.back, _camera.transform.rotation * Vector3.up); } public void Aim(Vector2 direction) { _unitView.AimCanvas.transform.LookAt( new Vector3(direction.x, 0, direction.y) + _unitView.transform.position); _direction = direction; } public HexCell PlaceItemAim(HexDirection direction) { if (_cell.GetNeighbor(direction).Color != Color) { _unitView.AimCanvas.SetActive(false); return null; } var cell = _cell.GetNeighbor(direction); _unitView.AimCanvas.transform.LookAt(cell.transform); return cell; } private void Damage(int dmg) { if (_defenceBonus == 0 && _hp - dmg <= 0f) { Death(); } if (_hp - dmg > _data.maxHP) { _hp = _data.maxHP; } if (_defenceBonus > 0) { return; } SetUpBonus(0, 0, BonusType.Defence); _hp -= dmg; UpdateBarCanvas(); } } }