using System.Collections.Generic;
|
using UnityEngine;
|
using System;
|
using DG.Tweening;
|
using Spine;
|
using System.Linq;
|
|
|
public class SkillBase
|
{
|
const float moveTime = 0.5f;
|
|
protected SkillEffect skillEffect;
|
|
protected HB427_tagSCUseSkill tagUseSkillAttack;
|
|
protected SkillConfig skillConfig;
|
|
protected bool isFinished = false;
|
|
protected BattleField battleField = null; // 战场
|
|
protected RectTransform targetNode = null; // 目标节点
|
|
protected BattleObject caster = null; // 施法者
|
|
protected bool startCounting = false;
|
|
protected bool pauseState = false;
|
|
protected int curFrame = 0;
|
|
protected List<GameNetPackBasic> packList;
|
|
protected SkillRecordAction otherSkillAction;
|
|
protected List<H0704_tagRolePackRefresh> dropPackList = new List<H0704_tagRolePackRefresh>();
|
|
protected List<HB405_tagMCAddExp> expPackList = new List<HB405_tagMCAddExp>();
|
|
protected bool moveFinished = false;
|
|
public SkillBase(BattleObject _caster, SkillConfig _skillCfg, HB427_tagSCUseSkill vNetData, List<GameNetPackBasic> _packList, BattleField _battleField = null)
|
{
|
caster = _caster;
|
skillConfig = _skillCfg;
|
tagUseSkillAttack = vNetData;
|
battleField = _battleField;
|
packList = _packList;
|
|
|
|
}
|
|
public virtual void Run()
|
{
|
if (startCounting)
|
{
|
curFrame++;
|
}
|
|
if (null != skillEffect)
|
{
|
skillEffect.Run();
|
}
|
|
if (otherSkillAction != null)
|
{
|
if (otherSkillAction.IsFinished())
|
{
|
otherSkillAction = null;
|
OnSkillFinished();
|
}
|
else
|
{
|
otherSkillAction.Run();
|
}
|
}
|
}
|
|
|
|
public void Pause()
|
{
|
pauseState = startCounting;
|
startCounting = false;
|
}
|
|
public void Resume()
|
{
|
startCounting = pauseState;
|
}
|
|
|
// 0·移动到距离目标n码,的距离释放(可配置,9999即原地释放,负数则是移动到人物背面,人物要转身)
|
// 1·移动到距离阵容位置n码的距离(如2号位,5号位)释放(即战场中央此类)
|
public virtual void Cast()
|
{
|
EventBroadcast.Instance.Broadcast<string, SkillConfig, TeamHero>(EventName.BATTLE_CAST_SKILL, battleField.guid, skillConfig, caster.teamHero);
|
|
// 高亮所有本次技能相关的目标
|
HighLightAllTargets();
|
|
// 距离配成负数要转身 TurnBack
|
switch (skillConfig.castMode)
|
{
|
case SkillCastMode.Self:
|
CastImpl(OnAttackFinish);
|
break;
|
case SkillCastMode.Enemy:
|
CastToEnemy();
|
break;
|
case SkillCastMode.Target:
|
CastToTarget();
|
break;
|
case SkillCastMode.Allies:
|
CastToAllies();
|
break;
|
case SkillCastMode.DashCast:
|
DashCast(OnAttackFinish);
|
break;
|
default:
|
Debug.LogError("暂时不支持其他的方式释放 有需求请联系策划 技能id:" + skillConfig.SkillID + " cast position " + skillConfig.CastPosition);
|
OnSkillFinished();
|
break;
|
}
|
|
}
|
|
// 冲撞攻击
|
protected void DashCast(Action _onComplete)
|
{
|
Debug.LogError("DashCast 还没实现");
|
// YYL TODO
|
|
// var entry = caster.motionBase.PlayAnimation(skillConfig.GetMotionName(), false);
|
// float animationTime = entry.AnimationTime;
|
|
// int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
|
|
// BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
|
|
// RectTransform targetTrans = battleField.GetTeamNode(battleCamp, mainTargetPosNum);
|
|
// var tweener = BattleUtility.MoveToTarget(caster.heroRectTrans, targetTrans, new Vector2(skillConfig.CastDistance, 0), animationTime * 0.9f, () =>
|
// {
|
// caster.motionBase.PlayAnimation(MotionName.idle, true);
|
// _onComplete?.Invoke();
|
// });
|
// battleField.battleTweenMgr.OnPlayTween(tweener);
|
}
|
|
protected void MoveToTarget(RectTransform target, Vector2 offset, float duration, Action onComplete = null)
|
{
|
// 原地释放
|
if (skillConfig.CastDistance >= 9999)
|
{
|
onComplete?.Invoke();
|
return;
|
}
|
|
caster.motionBase.PlayAnimation(MotionName.run, true);
|
var tweener = BattleUtility.MoveToTarget(caster.heroRectTrans, target, offset, duration, () =>
|
{
|
caster.motionBase.PlayAnimation(MotionName.idle, true);
|
onComplete?.Invoke();
|
});
|
battleField.battleTweenMgr.OnPlayTween(tweener);
|
}
|
|
protected void TurnBack(Action _onComplete, float forward)
|
{
|
if (skillConfig.CastDistance < 0)
|
{
|
// 转身
|
Vector3 scale = caster.heroGo.transform.localScale;
|
scale.x = Mathf.Abs(scale.x) * forward;
|
caster.heroGo.transform.localScale = scale;
|
}
|
_onComplete?.Invoke();
|
}
|
|
protected void CastToEnemy()
|
{
|
|
RectTransform target = battleField.GetTeamNode(caster.GetEnemyCamp(), skillConfig);
|
|
MoveToTarget(target, new Vector2(skillConfig.CastDistance, 0), moveTime, () =>
|
{
|
// 到位置转身(不一定非要转身 但是流程要写)
|
TurnBack(() =>
|
{
|
// 到达目标位置
|
CastImpl(() =>
|
{
|
TurnBack(
|
() =>
|
{
|
// 回到原来的位置
|
MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, moveTime,
|
OnAttackFinish);
|
}
|
, -1f);
|
});
|
}, -1f);
|
});
|
}
|
|
|
|
protected void CastToTarget()
|
{
|
// 目标是敌方主目标
|
if (tagUseSkillAttack.HurtCount <= 0)
|
{
|
Debug.LogError("技能攻击包没有目标 HurtCount <= 0");
|
OnSkillFinished();
|
return;
|
}
|
|
int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
|
|
BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
|
|
RectTransform targetTrans = battleField.GetTeamNode(battleCamp, mainTargetPosNum);
|
|
MoveToTarget(targetTrans, new Vector2(skillConfig.CastDistance, 0), moveTime, () =>
|
{
|
// 到位置转身(不一定非要转身 但是流程要写)
|
TurnBack(() =>
|
{
|
// 到达目标位置
|
CastImpl(() =>
|
{
|
TurnBack(
|
() =>
|
{
|
// 回到原来的位置
|
MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero, moveTime,
|
OnAttackFinish);
|
}
|
, -1f);
|
});
|
}, -1f);
|
});
|
}
|
|
protected virtual void OnAllAttackMoveFinished()
|
{
|
moveFinished = true;
|
}
|
|
protected void CastToAllies()
|
{
|
RectTransform target = battleField.GetTeamNode(caster.Camp, skillConfig);
|
|
MoveToTarget(target, new Vector2(skillConfig.CastDistance, 0), moveTime, () =>
|
{
|
// 到位置转身(不一定非要转身 但是流程要写)
|
TurnBack(() =>
|
{
|
// 到达目标位置
|
CastImpl(() =>
|
{
|
TurnBack(
|
() =>
|
{
|
// 回到原来的位置
|
MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum),
|
Vector2.zero, moveTime, OnAttackFinish);
|
}
|
, -1f);
|
});
|
}, -1f);
|
});
|
}
|
|
protected void OnAttackFinish()
|
{
|
TurnBack(null, 1f);
|
OnAllAttackMoveFinished();
|
caster.motionBase.PlayAnimation(MotionName.idle, true);
|
}
|
|
|
// 承载技能大部分的逻辑
|
protected TrackEntry CastImpl(Action onComplete = null)
|
{
|
// 播放施法动作
|
// onComplete是指施法动作播放完的回调 不代表是技能结束
|
// 具体技能结束的时间应该看技能对应的逻辑
|
// 这里只提供6个动作相关的函数
|
// OnSkillStart 动作第一帧
|
// OnStartSkillFrameEnd 前摇结束
|
// OnMiddleFrameStart 中摇开始
|
// OnMiddleFrameEnd 中摇结束
|
// OnFinalFrameStart 后摇开始
|
// OnFinalFrameEnd 后摇结束
|
return caster.motionBase.PlaySkillAnimation(skillConfig, this, onComplete);
|
}
|
|
// 技能开始
|
public void OnSkillStart()
|
{
|
skillEffect = SkillEffectFactory.CreateSkillEffect(
|
caster,
|
skillConfig,
|
tagUseSkillAttack
|
);
|
// if (skillEffect != null)
|
{
|
skillEffect.Play(OnHitTargets);
|
}
|
|
|
|
}
|
|
// 技能前摇帧结束
|
public virtual void OnStartSkillFrameEnd()
|
{
|
|
}
|
|
/// <summary>
|
/// 中摇开始 times=第几次循环 从0开始
|
/// </summary>
|
/// <param name="times"></param>
|
public virtual void OnMiddleFrameStart(int times)
|
{
|
// if (skillEffect != null)
|
{
|
skillEffect.OnMiddleFrameStart(times);
|
}
|
}
|
|
public virtual void OnMiddleFrameEnd(int times, int hitIndex)
|
{
|
// if (skillEffect != null)
|
{
|
skillEffect.OnMiddleFrameEnd(times, hitIndex);
|
}
|
}
|
|
/// <summary>
|
/// 后摇开始
|
/// </summary>
|
public virtual void OnFinalFrameStart()
|
{
|
// if (skillEffect != null)
|
{
|
skillEffect.OnFinalFrameStart();
|
}
|
}
|
|
/// <summary>
|
/// 后摇结束
|
/// </summary>
|
public virtual void OnFinalFrameEnd()
|
{
|
// if (skillEffect != null)
|
{
|
skillEffect.OnFinalFrameEnd();
|
}
|
|
HandleDead();
|
}
|
|
|
|
|
protected void HighLightAllTargets()
|
{
|
// 高亮所有目标
|
HashSet<BattleObject> highlightList = new HashSet<BattleObject>(battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack));
|
highlightList.Add(caster);
|
|
|
// 把这些BO全高亮 或者说把除了这些的都放在遮罩后面
|
// YYL TODO
|
}
|
|
// 命中目标后的回调 正常是以各技能的方式来处理的
|
protected virtual void OnHitTargets(int _hitIndex, List<HB427_tagSCUseSkill.tagSCUseSkillHurt> hitList)
|
{
|
for (int i = 0; i < hitList.Count; i++)
|
{
|
HB427_tagSCUseSkill.tagSCUseSkillHurt hurt = hitList[i];
|
|
BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID);
|
if (target == null)
|
{
|
Debug.LogError("目标为空 target == null ObjId : " + hurt.ObjID);
|
continue;
|
}
|
|
OnHitEachTarget(_hitIndex, target, hurt);
|
}
|
|
}
|
|
|
protected virtual void OnHitEachTarget(int _hitIndex, BattleObject target, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt)
|
{
|
// 伤害分布 (万分比)
|
int[] damageDivide = skillConfig.DamageDivide[_hitIndex];
|
|
long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx);
|
|
// 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
|
List<long> damageList = BattleUtility.DivideDamageToList(damageDivide, totalDamage);
|
// public uint ObjID;
|
// public uint AttackTypes; // 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
|
// public uint HurtHP; // 飘血值,求余亿部分
|
// public uint HurtHPEx; // 飘血值,整除亿部分
|
// public uint CurHP; // 更新剩余血量,求余亿部分
|
// public uint CurHPEx; // 更新剩余血量,整除亿部分
|
// public uint SuckHP; // 本次伤害转化的吸血量
|
// public uint BounceHP; // 本次伤害反弹的伤害量
|
|
|
// TODO YYL AttackTypes 要表现成什么样呢? 支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
|
target.Hurt(damageList, totalDamage, hurt, skillConfig);
|
|
// TODO YYL 这里是要做统一计算后再hurt跟suckhp还是怎样
|
caster.SuckHp(hurt.SuckHP, skillConfig);// 吸血
|
caster.HurtByReflect(hurt.BounceHP, skillConfig);// 反弹伤害
|
}
|
|
|
protected void HandleDead()
|
{
|
var deadPackList = BattleUtility.FindDeadPack(packList);
|
int deadCount = deadPackList.Count;
|
|
if (deadCount <= 0)
|
{
|
// 如果没死亡就不用管
|
return;
|
}
|
|
CheckAfterDeadhPack();
|
|
// 处理掉落包 提前distribute之后 PackManager才有掉落物 所以不跟assignexp一样distribute
|
foreach (var _dropPack in dropPackList)
|
{
|
PackageRegedit.Distribute(_dropPack);
|
packList.Remove(_dropPack);
|
}
|
|
// 获取掉落物品
|
var dropPack = PackManager.Instance.GetSinglePack(PackType.DropItem);
|
var itemDict = dropPack.GetAllItems();
|
List<ItemModel> itemList = new List<ItemModel>(
|
from item in itemDict.Values
|
where item != null && item.isAuction
|
select item);
|
|
|
|
// 分配掉落和经验
|
var dropAssign = AssignDrops(itemList, deadCount);
|
var expAssign = AssignExp(expPackList, deadCount);
|
|
// 构造 BattleDrops 并分配
|
for (int i = 0; i < deadCount; i++)
|
{
|
BattleObject deadTarget = battleField.battleObjMgr.GetBattleObject((int)deadPackList[i].ObjID);
|
List<ItemModel> itemModelDrops = dropAssign[i];
|
List<int> itemModelDropsIndexList = new List<int>(
|
from item in itemModelDrops select item.gridIndex);
|
BattleDrops battleDrops = new BattleDrops()
|
{
|
rectTransform = deadTarget.heroRectTrans,
|
dropItemPackIndex = itemModelDropsIndexList,
|
expDrops = expAssign[i]
|
};
|
deadTarget.PushDropItems(battleDrops);
|
}
|
|
// 分发死亡包
|
|
battleField.OnObjsDead(new List<HB422_tagMCTurnFightObjDead>(deadPackList));
|
foreach (var deadPack in deadPackList)
|
{
|
packList.Remove(deadPack);
|
}
|
deadPackList.Clear();
|
}
|
|
|
|
// 分配掉落
|
protected List<List<ItemModel>> AssignDrops(List<ItemModel> itemList, int deadCount)
|
{
|
var dropAssign = new List<List<ItemModel>>(deadCount);
|
for (int i = 0; i < deadCount; i++)
|
dropAssign.Add(new List<ItemModel>());
|
for (int i = 0; i < itemList.Count; i++)
|
dropAssign[i % deadCount].Add(itemList[i]);
|
return dropAssign;
|
}
|
|
// 分配经验:每个原始包都平均分配到每个死亡对象
|
protected List<List<HB405_tagMCAddExp>> AssignExp(List<HB405_tagMCAddExp> expList, int deadCount)
|
{
|
var expAssign = new List<List<HB405_tagMCAddExp>>(deadCount);
|
for (int i = 0; i < deadCount; i++)
|
expAssign.Add(new List<HB405_tagMCAddExp>());
|
|
foreach (var expPack in expList)
|
{
|
long totalExp = GeneralDefine.GetFactValue(expPack.Exp, expPack.ExpPoint);
|
long avgExp = totalExp / deadCount;
|
long remain = totalExp % deadCount;
|
|
for (int i = 0; i < deadCount; i++)
|
{
|
long assignExp = avgExp + (i < remain ? 1 : 0);
|
long expPoint = assignExp / 100000000;
|
long exp = assignExp % 100000000;
|
var newPack = new HB405_tagMCAddExp
|
{
|
Exp = (uint)exp,
|
ExpPoint = (uint)expPoint,
|
Source = expPack.Source // 保持原包来源
|
};
|
expAssign[i].Add(newPack);
|
}
|
packList.Remove(expPack);
|
}
|
return expAssign;
|
}
|
|
|
|
protected void CheckAfterDeadhPack()
|
{
|
List<int> removeIndexList = new List<int>();
|
for (int i = 0; i < packList.Count; i++)
|
{
|
var pack = packList[i];
|
|
// 复活基本都靠技能包
|
if (pack is CustomHB426CombinePack)
|
{
|
var combinePack = pack as CustomHB426CombinePack;
|
if (combinePack.startTag.Tag.StartsWith("Skill_"))
|
{
|
break; // 找到技能包就不需要再处理了
|
}
|
}
|
else if (pack is H0704_tagRolePackRefresh)
|
{
|
var h0704Pack = pack as H0704_tagRolePackRefresh;
|
if (h0704Pack.PackType == (byte)PackType.DropItem)
|
{
|
// 掉落的
|
if (h0704Pack.IsBind == 1)
|
{
|
// 掉落的物品
|
dropPackList.Add(h0704Pack);
|
removeIndexList.Add(i);
|
}
|
else if (h0704Pack.IsBind == 0)
|
{
|
// 替换的
|
}
|
}
|
}
|
else if (pack is HB405_tagMCAddExp)
|
{
|
var h405Pack = pack as HB405_tagMCAddExp;
|
|
//B4 05 获得经验 #tagMCAddExp 通知获得的经验,
|
//可用于做经验获得表现 Source = 2 时为主线击杀怪物获得经验
|
if (h405Pack.Source == 2)
|
{
|
expPackList.Add(h405Pack);
|
removeIndexList.Add(i);
|
}
|
}
|
|
}
|
|
for (int i = removeIndexList.Count - 1; i >= 0; i--)
|
{
|
packList.RemoveAt(removeIndexList[i]);
|
}
|
}
|
public virtual bool IsFinished()
|
{
|
if (skillEffect != null)
|
{
|
if (!skillEffect.IsFinished())
|
{
|
return false;
|
}
|
else
|
{
|
OnSkillFinished();
|
skillEffect = null;
|
}
|
}
|
|
return isFinished && moveFinished;
|
}
|
|
public virtual void ForceFinished()
|
{
|
skillEffect?.ForceFinished();
|
isFinished = true;
|
moveFinished = true;
|
|
while (packList.Count > 0)
|
{
|
var pack = packList[0];
|
packList.RemoveAt(0);
|
|
if (pack is CustomHB426CombinePack)
|
{
|
var combinePack = pack as CustomHB426CombinePack;
|
if (combinePack.startTag.Tag.StartsWith("Skill_"))
|
{
|
BattleDebug.LogError("other skill casting " + combinePack.startTag.Tag);
|
otherSkillAction = combinePack.CreateSkillAction();
|
|
// 强制结束其他技能
|
otherSkillAction.ForceFinish();
|
return;
|
}
|
}
|
PackageRegedit.Distribute(pack);
|
}
|
}
|
|
public void OnSkillFinished()
|
{
|
if (skillEffect != null)
|
{
|
if (!skillEffect.IsFinished())
|
{
|
return;
|
}
|
}
|
|
while (packList.Count > 0)
|
{
|
var pack = packList[0];
|
packList.RemoveAt(0);
|
|
if (pack is CustomHB426CombinePack)
|
{
|
var combinePack = pack as CustomHB426CombinePack;
|
if (combinePack.startTag.Tag.StartsWith("Skill_"))
|
{
|
BattleDebug.LogError("other skill casting " + combinePack.startTag.Tag);
|
otherSkillAction = combinePack.CreateSkillAction();
|
return;
|
}
|
}
|
PackageRegedit.Distribute(pack);
|
}
|
|
isFinished = true;
|
}
|
}
|