using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using UnityEngine;
|
using DG.Tweening;
|
using DG.Tweening.Core;
|
using DG.Tweening.Plugins.Options;
|
using Spine.Unity;
|
using UnityEngine.UI;
|
using System.Linq;
|
|
public enum BattleCamp
|
{
|
Red,
|
Blue
|
}
|
|
public enum BattleState
|
{
|
None = 0,
|
Stunned = 1 << 0,
|
Poisoned = 1 << 1,
|
Bleeding = 1 << 2,
|
Silenced = 1 << 3,
|
Frozen = 1 << 4,
|
Burned = 1 << 5
|
}
|
|
public class BattleObject
|
{
|
public BattleField battleField;
|
|
public int BattleObjectId { get; set; }
|
|
public BattleCamp Camp { get; protected set; }
|
|
public TeamHero teamHero { get; protected set; }
|
|
// public BuffMgr buffMgr;
|
|
protected MotionBase motionBase;
|
|
public GameObject heroGo
|
{
|
get;
|
private set;
|
}
|
|
public BattleObject(BattleField _battleField)
|
{
|
battleField = _battleField;
|
}
|
|
public virtual void Init(GameObject _heroGo, TeamHero _teamHero, BattleCamp _camp)
|
{
|
heroGo = _heroGo;
|
teamHero = _teamHero;
|
Camp = _camp;
|
motionBase = new MotionBase();
|
motionBase.Init(heroGo.GetComponentInChildren<SkeletonGraphic>(true));
|
}
|
|
|
|
public virtual void Run()
|
{
|
motionBase.Run();
|
}
|
|
public virtual void Pause()
|
{
|
motionBase.Pause();
|
}
|
|
public virtual void Resume()
|
{
|
motionBase.Resume();
|
}
|
|
public virtual void Destroy()
|
{
|
if (heroGo != null)
|
{
|
GameObject.DestroyImmediate(heroGo);
|
heroGo = null;
|
}
|
|
motionBase.Release();
|
motionBase = null;
|
teamHero = null;
|
BattleObjectId = 0;
|
}
|
|
// 眩晕
|
public bool IsStunned()
|
{
|
return teamHero.isStunned;
|
}
|
|
// 冰冻
|
public bool IsFrozen()
|
{
|
return teamHero.isFrozen;
|
}
|
|
// 石化
|
public bool IsStoned()
|
{
|
return teamHero.isStoned;
|
}
|
|
// // 禁锢
|
// public bool IsConfined()
|
// {
|
// return false;
|
// }
|
|
// 被沉默
|
public bool IsSlient()
|
{
|
return teamHero.isSlient;
|
}
|
|
// 被缴械
|
public bool IsDisarmed()
|
{
|
return teamHero.isDisarmed;
|
}
|
|
// 是否无敌
|
public bool IsInvincable()
|
{
|
return teamHero.isInvinceble;
|
}
|
|
// 是否死亡
|
public bool IsDead()
|
{
|
return teamHero.isDead;
|
}
|
|
// 是否被控住了
|
public bool IsCrowdControlled()
|
{
|
return IsStunned() || IsStoned() || IsFrozen();
|
}
|
|
public virtual bool IsCanCastSkill()
|
{
|
// 被控住
|
if (IsCrowdControlled())
|
{
|
return false;
|
}
|
|
// 被沉默
|
if (IsSlient())
|
{
|
return false;
|
}
|
|
// 看看怒气是否达到释放要求
|
return teamHero.rage >= 100;
|
}
|
|
public virtual bool IsCanNormalAttack()
|
{
|
// 被控住
|
if (IsCrowdControlled())
|
{
|
return false;
|
}
|
|
// 缴械
|
if (IsDisarmed())
|
{
|
return false;
|
}
|
|
return true;
|
}
|
|
public virtual void TakeDamage(List<int> damageValues)
|
{
|
if (IsDead())
|
return;
|
|
PopDamage(damageValues);
|
|
motionBase.PlayAnimation(MotionName.hit, false);
|
|
// 计算伤害
|
int totalDamage = 0;
|
foreach (var damage in damageValues)
|
{
|
totalDamage += damage;
|
}
|
|
|
// 扣血
|
teamHero.curHp -= totalDamage;
|
|
// 其实这里应该是等服务器发death的action
|
// if (IsDead())
|
// {
|
// OnDeath();
|
// }
|
|
}
|
|
// 闪避开始
|
public virtual void OnDodgeBegin()
|
{
|
float pingpongTime = 0.2f;
|
RectTransform rectTrans = heroGo.GetComponent<RectTransform>();
|
rectTrans.DOAnchorPos(new Vector3(-50, 50, 0), pingpongTime)
|
.SetEase(Ease.OutCubic);
|
}
|
|
// 闪避结束
|
public virtual void OnDodgeEnd()
|
{
|
float pingpongTime = 0.2f;
|
RectTransform rectTrans = heroGo.GetComponent<RectTransform>();
|
rectTrans.DOAnchorPos(Vector3.zero, pingpongTime)
|
.SetEase(Ease.OutCubic);
|
}
|
|
protected virtual void OnDeath()
|
{
|
motionBase.OnOtherAnimationComplete = OnOtherAnimationComplete;
|
motionBase.PlayAnimation(MotionName.dead, false);
|
}
|
|
protected virtual void OnOtherAnimationComplete(MotionName motionName)
|
{
|
if (motionName == MotionName.dead)
|
{
|
OnDeadAnimationComplete();
|
}
|
}
|
|
protected virtual void OnDeadAnimationComplete()
|
{
|
// 或许看看溶解特效? YYL TODO
|
heroGo.SetActive(false);
|
}
|
|
// 伤害还要看 是否闪避 暴击 and so on 需要有一个DamageType 服务器应该会给
|
protected virtual void PopDamage(List<int> damageValues)
|
{
|
// 其实应该通知出去给UI界面解耦 让UI界面自己来显示的 YYL TODO
|
// 播放伤害数字
|
// 这里可以实现一个伤害数字的弹出效果
|
// 比如使用一个UI组件来显示伤害数字
|
foreach (var damage in damageValues)
|
{
|
Debug.Log($"Damage: {damage}");
|
}
|
|
EventBroadcast.Instance.Broadcast<BattleObject, List<int>>(EventName.BATTLE_DAMAGE_TAKEN, this, damageValues);
|
}
|
|
public void PlaySkill(SkillConfig skillConfig, List<Dictionary<int, List<int>>> damageList, Action _onComplete)
|
{
|
bool moveToTarget = true;
|
|
if (moveToTarget)
|
{
|
int targetId = damageList[0].First().Key;
|
BattleObject _targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
|
|
RectTransform selfRect = heroGo.GetComponent<RectTransform>();
|
RectTransform targetRect = _targetObj.heroGo.GetComponent<RectTransform>();
|
Vector2 curAnchoredPos = selfRect.anchoredPosition;
|
|
MoveToTargetUI(selfRect, targetRect, new Vector2(100f, 0f), () =>
|
{
|
PlaySkillAnimation(skillConfig, damageList, () =>
|
{
|
// 回到原位置
|
selfRect.DOAnchorPos(curAnchoredPos, 0.2f)
|
.SetEase(Ease.Linear)
|
.OnComplete(() => {
|
_onComplete?.Invoke();
|
});
|
});
|
});
|
}
|
else
|
{
|
PlaySkillAnimation(skillConfig, damageList, _onComplete);
|
}
|
}
|
|
protected void MoveToTargetUI(RectTransform selfRect, RectTransform targetRect, Vector2 offset, Action _onComplete)
|
{
|
// 1. 目标的本地坐标转为世界坐标
|
Vector3 targetWorldPos = targetRect.TransformPoint(targetRect.anchoredPosition + offset);
|
|
// 2. 世界坐标转为自己父节点下的本地坐标
|
RectTransform parentRect = selfRect.parent as RectTransform;
|
Vector2 targetAnchoredPos;
|
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
parentRect,
|
RectTransformUtility.WorldToScreenPoint(null, targetWorldPos),
|
null,
|
out targetAnchoredPos);
|
|
// 3. DOTween 移动
|
selfRect.DOAnchorPos(targetAnchoredPos, 0.2f)
|
.SetEase(Ease.Linear)
|
.OnComplete(() => _onComplete?.Invoke());
|
}
|
|
|
protected void PlaySkillAnimation(SkillConfig skillConfig, List<Dictionary<int, List<int>>> damageList, Action _onComplete)
|
{
|
|
// 关键帧列表
|
List<int> keyFrameList = new List<int>() { 15 };
|
motionBase.OnAttackHitEvent = (int _frame) =>
|
{
|
Dictionary<int, List<int>> oneRoundDamage = damageList[keyFrameList.IndexOf(_frame)];
|
|
foreach (var kvp in oneRoundDamage)
|
{
|
int targetId = kvp.Key;
|
List<int> damageValues = kvp.Value;
|
|
BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
|
if (targetObj != null && !targetObj.IsDead())
|
{
|
targetObj.TakeDamage(damageValues);
|
}
|
}
|
};
|
|
motionBase.OnAttackAnimationComplete = () =>
|
{
|
_onComplete?.Invoke();
|
|
motionBase.OnAttackHitEvent = null;
|
motionBase.OnAttackAnimationComplete = null;
|
|
// 死亡确定其实不应该在这里进行触发 应该由服务器下发 YYL TODO
|
|
#if UNITY_EDITOR
|
// 暂时的处理
|
HashSet<int> hitTargets = new HashSet<int>();
|
|
foreach (var dmgDict in damageList)
|
{
|
foreach (var kvp in dmgDict)
|
{
|
int targetId = kvp.Key;
|
hitTargets.Add(targetId);
|
}
|
}
|
|
foreach (int targetId in hitTargets)
|
{
|
BattleObject targetObj = battleField.battleObjMgr.GetBattleObject(targetId);
|
if (targetObj != null && targetObj.IsDead())
|
{
|
targetObj.OnDeath();
|
}
|
}
|
#endif
|
};
|
|
motionBase.PlayAnimationEx(MotionName.attack, false, keyFrameList);
|
}
|
|
#if UNITY_EDITOR
|
public void EditorRevive()
|
{
|
teamHero.curHp = 100;
|
heroGo.SetActive(true);
|
motionBase.PlayAnimation(MotionName.idle, true);
|
}
|
|
public List<int> TryAttack(BattleObject obj, SkillConfig skillConfig)
|
{
|
List<int> damageList = new List<int>();
|
|
int totalDamage = teamHero.attack - obj.teamHero.defense;
|
|
int damage1 = (int)((float)totalDamage * 0.3f);
|
|
int damage2 = (int)((float)totalDamage * 0.25f);
|
|
int damage3 = totalDamage - damage1 - damage2;
|
|
damageList.Add(damage1);
|
damageList.Add(damage2);
|
damageList.Add(damage3);
|
|
return damageList;
|
}
|
#endif
|
|
}
|