using System; using System.Collections.Generic; using System.Linq; using AI; using Chars; using Data; using DefaultNamespace; using DG.Tweening; using HexFiled; using Items; using Sirenix.Utilities; using UnityEngine; using Weapons; using Object = UnityEngine.Object; namespace Units { public class Unit { private GameObject _instance; private List _inventory; private List _inventoryDefence; private AnimLength _animLength; private HexCell _cell; private HexGrid _hexGrid; public event Action OnPlayerSpawned; private Animator _animator; private UnitInfo _data; private int _hp; private int _mana; private Weapon _weapon; private Vector2 _direction; private bool _isCapturing; private bool _isInfiniteMana; private int _defenceBonus; private Camera _camera; private UnitColor _easyCaptureColor; public bool IsStaned; public bool isSwitched; public int AttackBonus => _weapon.modifiedDamage - _weapon.damage; public int DefenceBonus => _defenceBonus; public bool IsBusy { get; set; } public UnitView UnitView { get; private set; } public bool IsAlive { get; private set; } public bool IsHardToCapture { get; private set; } public UnitColor Color => _data.color; public int InventoryCapacity => _data.inventoryCapacity; public event Action OnItemPickUp; public event Action OnDeath; public BarCanvas BarCanvas => UnitView.BarCanvas; public GameObject Instance => _instance; public UnitInfo Data => _data; public int Mana => _mana; public int Hp => _hp; public List Inventory => _inventory; public List InventoryDefence => _inventoryDefence; public Weapon Weapon => _weapon; public bool IsPlayer => _data.isPlayer; public Animator Animator => _animator; public bool IsVisible { get; private set; } 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; _easyCaptureColor = UnitColor.Grey; } 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; case BonusType.Magnet: var col = UnitView.gameObject.GetComponent(); var defRadius = col.radius; col.radius = value * HexGrid.HexDistance; TimerHelper.Instance.StartTimer(() => col.radius = defRadius, duration); break; case BonusType.Mana: _isInfiniteMana = true; TimerHelper.Instance.StartTimer(() => _isInfiniteMana = false, duration); break; case BonusType.Invisible: IsVisible = false; UnitView.SetInvisible(IsVisible); TimerHelper.Instance.StartTimer(() => { IsVisible = true; UnitView.SetInvisible(IsVisible); }, duration); break; default: break; } } public void SetTimeScale(float scale) { _animator.speed = scale; } public void Retreet(HexDirection dir) { if (!_isCapturing) return; var openList = _cell.GetListNeighbours().Where(x => x != null && x.Color == _data.color).ToList(); if (!openList.Contains(_cell.GetNeighbor(dir))) { return; } IsBusy = false; IsHardToCapture = false; UnitView.StopHardCapture(); Move(dir); } public void Move(HexDirection direction) { if (_cell.GetNeighbor(direction) == null || _cell.GetNeighbor(direction).BuildingInstance != null || IsBusy || IsHardToCapture || (_cell.GetNeighbor(direction).Color != Color && HexManager.UnitCurrentCell.TryGetValue(_cell.GetNeighbor(direction).Color, out var value) && value.cell.Equals(_cell.GetNeighbor(direction)))) return; if (_cell.GetNeighbor(direction).Color == _data.color || (_cell.GetNeighbor(direction).Color == _easyCaptureColor && _easyCaptureColor != UnitColor.Grey)) { 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); } public void SetCell(HexCell cell, bool isInstanceTrans = false, bool isPaintingHex = false) { _cell = cell; HexManager.UnitCurrentCell[Color] = (cell, this); if (!isInstanceTrans) { IsBusy = true; _instance.transform.DOMove(_cell.transform.position, _animLength.SuperJump) .OnComplete(() => IsBusy = false); } else { _instance.transform.DOMove(_cell.transform.position, 0.5f).SetEase(Ease.Linear); } if (isPaintingHex) { cell.PaintHex(Color, true); } } public void SetEasyColor(UnitColor color, float time) { _easyCaptureColor = color; if (time > 0f) { TimerHelper.Instance.StartTimer(() => _easyCaptureColor = UnitColor.Grey, time); } } private void CaptureHex() { if (!_isInfiniteMana) { 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; case "Super_Attack": _animLength.SuperJump = clip.length; break; default: break; } } } public void Spawn(HexCoordinates hexCoordinates, HexCell spawnCell = null) { if (!IsAlive) { _cell = spawnCell != null ? spawnCell : _hexGrid.GetCellFromCoord(hexCoordinates); IsVisible = true; _cell.PaintHex(_data.color, true); _cell.GetListNeighbours().ForEach(x => { x?.PaintHex(Color, true); }); _inventory = new List(); _inventoryDefence = 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.AddComponent(); UnitView.SetUp(_weapon, RegenMana, _data.manaRegen, CaptureHex, this, _hexGrid.HardCaptureTime); SetAnimLength(); MusicController.Instance.AddAudioSource(_instance); _mana = _data.maxMana; _hp = _data.maxHP; SetUpActions(); _weapon.SetModifiedDamage(0); IsBusy = false; OnPlayerSpawned?.Invoke(this); } } private void RegenMana() { _mana += _data.manaRegen; UpdateBarCanvas(); } public bool CanPickUpItem(Item item) { switch (item.Type) { case ItemType.ATTACK: if (_inventory.Count < _data.inventoryCapacity / 2) { return true; } break; case ItemType.DEFENCE: if (_inventoryDefence.Count < _data.inventoryCapacity / 2) { return true; } break; default: throw new ArgumentOutOfRangeException(); } return false; } public void PickUpItem(ItemContainer item) { switch (item.Item.Type) { case ItemType.ATTACK: if (_inventory.Count < _data.inventoryCapacity / 2) { _inventory.Add(item); _cell.Item = null; } break; case ItemType.DEFENCE: if (_inventoryDefence.Count < _data.inventoryCapacity / 2) { _inventoryDefence.Add(item); _cell.Item = null; } break; default: throw new ArgumentOutOfRangeException(); } OnItemPickUp?.Invoke(item); } public void UseItem(Item item) { if (item.Type == ItemType.ATTACK) { var i = _inventory.First(i => i.Item == item); _inventory.Remove(i); } else { var i = _inventoryDefence.First(i => i.Item == item); _inventoryDefence.Remove(i); } } private void MoveEnd() { IsBusy = false; _animator.SetBool("isMoving", IsBusy); if (!_isCapturing) { return; } if (IsHardToCapture) { UnitView.HardCaptureHex(_cell); } else { CaptureHex(); } } private void AttackEnd() { IsBusy = false; UpdateBarCanvas(); } private void Attacking() { Aim(_direction); _weapon.Fire(_instance.transform, _direction, this); } 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); var hexToPaint = HexManager.CellByColor[Color]; _animator.SetTrigger("Death"); var vfx = VFXController.Instance.PlayEffect(HexGrid.Colors[Color].VFXDeathPrefab, _instance.transform.position); TimerHelper.Instance.StartTimer(() => { HexManager.PaintHexList(hexToPaint.Where(x => x.Color == Color).ToList(), UnitColor.Grey); Object.Destroy(_instance); OnDeath?.Invoke(this); }, _animLength.Death); 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; } _hp -= dmg; UpdateBarCanvas(); } } }