|  |  |  | using System; | 
					
						
							|  |  |  | using System.Collections.Generic; | 
					
						
							|  |  |  | using UnityEngine; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Game | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     public class SkillHitInfo | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         public AttackBehaviour SkillParam; | 
					
						
							|  |  |  |         public Vector3 HitDir; | 
					
						
							|  |  |  |         public int OwnerEntity; | 
					
						
							|  |  |  |         public int HitEntity; | 
					
						
							|  |  |  |         public float AttackRate; | 
					
						
							|  |  |  |         public float StunkRate; | 
					
						
							|  |  |  |         public string SkillId; //是否技能造成的伤害 | 
					
						
							|  |  |  |         public bool IsBreak; | 
					
						
							|  |  |  |         public int HitIndex; | 
					
						
							|  |  |  |         public int HitCount; | 
					
						
							|  |  |  |         public float Rank; | 
					
						
							|  |  |  |         public bool Continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public class ClipAttack : TimelineClipBase | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         private AttackClip _mRealAsset; | 
					
						
							|  |  |  |         private readonly List<SkillHitInfo> _tempHitInfoList = new List<SkillHitInfo>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public override void OnEnter() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             _mRealAsset = Asset as AttackClip; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public override void OnStay() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var param = _mRealAsset.template; | 
					
						
							|  |  |  |             var skill = Owner.skill; | 
					
						
							|  |  |  |             var castShape = param.Shape; | 
					
						
							|  |  |  |             var castDir = Util.IgnoreY(skill.CastDir); | 
					
						
							|  |  |  |             var rot = Quaternion.FromToRotation(Vector3.right, castDir); | 
					
						
							|  |  |  |             var castPos = rot * castShape.Offset + Owner.Pos(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (param.throwOnly) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 //投技 无需检测 | 
					
						
							|  |  |  |                 var targetId = Owner.skill.ThrowTarget; | 
					
						
							|  |  |  |                 var target = Util.GetEntity(targetId); | 
					
						
							|  |  |  |                 if (target is null) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //排除重复命中 | 
					
						
							|  |  |  |                 var hitKey = new Tuple<int, int>(target.ID(), param.hitId); | 
					
						
							|  |  |  |                 if (skill.HitSet.Contains(hitKey)) return; | 
					
						
							|  |  |  |                 skill.HitSet.Add(hitKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var skillHitInfo = new SkillHitInfo | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     SkillParam = param, | 
					
						
							|  |  |  |                     AttackRate = 1, | 
					
						
							|  |  |  |                     StunkRate = 1, | 
					
						
							|  |  |  |                     OwnerEntity = Owner.ID(), | 
					
						
							|  |  |  |                     HitEntity = Owner.skill.ThrowTarget, | 
					
						
							|  |  |  |                     HitDir = GetHitDir(param.hitDirType, target, castPos, castDir), | 
					
						
							|  |  |  |                     SkillId = skill.SkillId.Value, | 
					
						
							|  |  |  |                     IsBreak = true, | 
					
						
							|  |  |  |                     Rank = 1, | 
					
						
							|  |  |  |                     HitIndex = 1, | 
					
						
							|  |  |  |                     HitCount = 1, | 
					
						
							|  |  |  |                     Continue = false, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |                 if (!string.IsNullOrEmpty(skillHitInfo.SkillId)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     var skillCfg = Util.GetSkillMasterConfigData(skillHitInfo.SkillId); | 
					
						
							|  |  |  |                     if (skillCfg != null) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         var featureSkill = skillCfg.GetFeatureSkill(); | 
					
						
							|  |  |  |                         skillHitInfo.AttackRate = featureSkill.AttackRate; | 
					
						
							|  |  |  |                         skillHitInfo.StunkRate = featureSkill.StunRate; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 skill.HitInfo.Enqueue(skillHitInfo); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Util.DrawShape(castShape, castPos, castDir, Color.red); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             _tempHitInfoList.Clear(); | 
					
						
							|  |  |  |             var (rankMin, rankMax) = (float.MaxValue, float.MinValue); | 
					
						
							|  |  |  |             Util.ForeachEnemies(Owner.Team(), target => | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 //检测命中 | 
					
						
							|  |  |  |                 var isHit = UtilShape.IsOverlap(castShape, castPos, castDir, target.hp.HitBoxShape, target.Pos()); | 
					
						
							|  |  |  |                 if (!isHit) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //排除重复命中 | 
					
						
							|  |  |  |                 var hitKey = new Tuple<int, int>(target.ID(), param.hitId); | 
					
						
							|  |  |  |                 if (skill.HitSet.Contains(hitKey)) return; | 
					
						
							|  |  |  |                 skill.HitSet.Add(hitKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //计算结算顺序rank | 
					
						
							|  |  |  |                 var rank = GetRank(param.hitOrderType, target, castPos, castDir); | 
					
						
							|  |  |  |                 if (rank < rankMin) rankMin = rank; | 
					
						
							|  |  |  |                 if (rank > rankMax) rankMax = rank; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var skillHitInfo = new SkillHitInfo | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     SkillParam = param, | 
					
						
							|  |  |  |                     AttackRate = 1, | 
					
						
							|  |  |  |                     StunkRate = 1, | 
					
						
							|  |  |  |                     OwnerEntity = Owner.ID(), | 
					
						
							|  |  |  |                     HitEntity = target.ID(), | 
					
						
							|  |  |  |                     HitDir = GetHitDir(param.hitDirType, target, castPos, castDir), | 
					
						
							|  |  |  |                     SkillId = skill.SkillId.Value, | 
					
						
							|  |  |  |                     IsBreak = true, | 
					
						
							|  |  |  |                     Rank = rank, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |                 if (!string.IsNullOrEmpty(skillHitInfo.SkillId)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     var skillCfg = Util.GetSkillMasterConfigData(skillHitInfo.SkillId); | 
					
						
							|  |  |  |                     if (skillCfg != null) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         var featureSkill = skillCfg.GetFeatureSkill(); | 
					
						
							|  |  |  |                         skillHitInfo.AttackRate = featureSkill.AttackRate; | 
					
						
							|  |  |  |                         skillHitInfo.StunkRate = featureSkill.StunRate; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 _tempHitInfoList.Add(skillHitInfo); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             _tempHitInfoList.Sort((l, r) => l.Rank.CompareTo(r.Rank)); | 
					
						
							|  |  |  |             var hitNum = _tempHitInfoList.Count; | 
					
						
							|  |  |  |             var rankGroup = 0; | 
					
						
							|  |  |  |             var rankRange = rankMax - rankMin; //rank范围大小 | 
					
						
							|  |  |  |             var rankRangeEach = rankRange / GameConst.HitMaxSettle; //单个group的rank范围大小 | 
					
						
							|  |  |  |             for (var i = 0; i < hitNum; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var info = _tempHitInfoList[i]; | 
					
						
							|  |  |  |                 info.HitIndex = i; | 
					
						
							|  |  |  |                 info.HitCount = hitNum; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var rate = (info.Rank - rankMin) / rankRange; | 
					
						
							|  |  |  |                 if (rate > rankGroup * rankRangeEach) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     rankGroup++; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     info.Continue = true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 skill.HitInfo.Enqueue(info); | 
					
						
							|  |  |  |                 if (!info.SkillParam.isAoe) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private Vector3 GetHitDir(EHitDirType hitOrderType, GameEntity target, Vector3 castPos, Vector3 castDir) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var targetPos = target.Pos(); | 
					
						
							|  |  |  |             var targetDir = targetPos - castPos; | 
					
						
							|  |  |  |             targetDir.y = 0; | 
					
						
							|  |  |  |             switch (hitOrderType) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 case EHitDirType.Center: | 
					
						
							|  |  |  |                     //从中心到外围 | 
					
						
							|  |  |  |                     return targetDir; | 
					
						
							|  |  |  |                 case EHitDirType.Backward: | 
					
						
							|  |  |  |                     //从前往后 | 
					
						
							|  |  |  |                     return -castDir; | 
					
						
							|  |  |  |                 case EHitDirType.Forward: | 
					
						
							|  |  |  |                     //从后往前 | 
					
						
							|  |  |  |                     return castDir; | 
					
						
							|  |  |  |                 case EHitDirType.Clockwise: | 
					
						
							|  |  |  |                     //顺时针 | 
					
						
							|  |  |  |                     return GetDirRot(targetDir, true); | 
					
						
							|  |  |  |                 case EHitDirType.CounterClockwise: | 
					
						
							|  |  |  |                     //逆时针 | 
					
						
							|  |  |  |                     return GetDirRot(targetDir, false); | 
					
						
							|  |  |  |                 case EHitDirType.CenterForward: | 
					
						
							|  |  |  |                     //中心向前 | 
					
						
							|  |  |  |                     return targetDir + castDir; | 
					
						
							|  |  |  |                 case EHitDirType.ClockwiseForward: | 
					
						
							|  |  |  |                     //顺时针向前 | 
					
						
							|  |  |  |                     return GetDirRot(targetDir, true) + castDir; | 
					
						
							|  |  |  |                 case EHitDirType.CounterClockwiseForward: | 
					
						
							|  |  |  |                     //逆时针向前 | 
					
						
							|  |  |  |                     return GetDirRot(targetDir, false) + castDir; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return targetDir; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private Vector3 GetDirRot(Vector3 targetDir, bool isClockwise) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var sub = (Owner.move.IsRight ? -1 : 1) * (isClockwise ? 1 : -1); | 
					
						
							|  |  |  |             return Vector3.Cross(targetDir, Vector3.up * sub).normalized; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private float GetRank(EHitOrderType hitOrderType, GameEntity target, Vector3 castPos, Vector3 castDir) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             //小在前 | 
					
						
							|  |  |  |             var targetPos = target.Pos(); | 
					
						
							|  |  |  |             var targetDir = targetPos - castPos; | 
					
						
							|  |  |  |             switch (hitOrderType) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 case EHitOrderType.Center: | 
					
						
							|  |  |  |                     //按距离 | 
					
						
							|  |  |  |                     return Vector3.Distance(targetPos, castPos); | 
					
						
							|  |  |  |                 case EHitOrderType.Backward: | 
					
						
							|  |  |  |                     //从前往后 | 
					
						
							|  |  |  |                     return -Vector3.Dot(targetDir, castDir); | 
					
						
							|  |  |  |                 case EHitOrderType.Forward: | 
					
						
							|  |  |  |                     //从后往前 | 
					
						
							|  |  |  |                     return Vector3.Dot(targetDir, castDir); | 
					
						
							|  |  |  |                 case EHitOrderType.Clockwise: | 
					
						
							|  |  |  |                     //顺时针 | 
					
						
							|  |  |  |                     return -GetRankRot(castDir, targetDir); | 
					
						
							|  |  |  |                 case EHitOrderType.CounterClockwise: | 
					
						
							|  |  |  |                     //逆时针 | 
					
						
							|  |  |  |                     return GetRankRot(castDir, targetDir); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private float GetRankRot(Vector3 castDir, Vector3 targetDir) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var rot = Quaternion.FromToRotation(-castDir, Vector3.right); | 
					
						
							|  |  |  |             return Util.Vec3ToRot(rot * targetDir) * (Owner.move.IsRight ? 1 : -1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |