using System; using System.Collections.Generic; using System.Text; using UnityEngine; using Thousandto.Cfg.Data; using Thousandto.Code.Logic.LocalPlayerBT; using Thousandto.Core.Asset; using Thousandto.Core.Base; using Thousandto.Code.Center; using Thousandto.Code.Global; using Thousandto.Plugins.Common.UniScene; #pragma warning disable 0219 #pragma warning disable 0168 #pragma warning disable 0162 namespace Thousandto.Code.Logic { /// <summary> /// 战斗的一些通用函数 /// </summary> public static class CombatUtil { //查询目标时的缓存信息 private static List<SearchTargetInfo> _searchCache = new List<SearchTargetInfo>(32); private static List<SearchCollectionInfo> _searchCollectCache = new List<SearchCollectionInfo>(32); //用于排序的比较函数 private static Comparison<SearchTargetInfo> compareFunc; private static Comparison<SearchTargetInfo> compareAngleFunc; private static Comparison<SearchCollectionInfo> compareCollectFunc; //静态构造函数 static CombatUtil() { compareFunc = (left, right) => { return left.distanceSq.CompareTo(right.distanceSq); }; compareAngleFunc = (left, right) => { return left.angle.CompareTo(right.angle); }; compareCollectFunc = (left, right) => { return left.distanceSq.CompareTo(right.distanceSq); }; } public static SkillHitType ConvertToHitType(int serverData) { if (IsBeatHit(serverData)) { return SkillHitType.HitStiff; } else if (IsBeatBack(serverData)) { return SkillHitType.HitBack; } else if (IsBeatFly(serverData)) { return SkillHitType.HitFly; } else if (IsBeatGrap(serverData)) { return SkillHitType.HitGrab; } return SkillHitType.None; } //受击僵直 public static bool IsBeatHit(int type) { if ((type & (int)SkillEffectType.AttackHit) != 0) return true; return false; } //击退 public static bool IsBeatBack(int type) { if ((type & (int)SkillEffectType.AttackBack) != 0) return true; return false; } //击飞 public static bool IsBeatFly(int type) { if ((type & (int)SkillEffectType.AttackFly) != 0) return true; return false; } //抓取 public static bool IsBeatGrap(int type) { if ((type & (int)SkillEffectType.AttackGrab) != 0) return true; return false; } //暴击 public static bool IsCrit(int type) { if ((type & (uint)SkillEffectType.Crit) != 0) return true; return false; } //会心 public static bool IsHuiXin(int type) { if ((type & (uint)SkillEffectType.HuiXin) != 0) return true; return false; } //致命一击 public static bool IsZhuiJi(int type) { if ((type & (uint)SkillEffectType.ZhuiJi) != 0) return true; return false; } //幸运一击 public static bool IsLianJi(int type) { if ((type & (uint)SkillEffectType.LianJi) != 0) return true; return false; } //无敌 public static bool IsInvincible(int type) { if ((type & (uint)SkillEffectType.Invincible) != 0) return true; return false; } //免疫 public static bool IsImmune(int type) { if ((type & (uint)SkillEffectType.Immune) != 0) return true; return false; } //是否是多倍攻击 public static bool IsMultipleAtk(int type) { if ((type & (uint)SkillEffectType.MultipleAtk) != 0) return true; return false; } //获取技能寻路距离 public static float GetSkillFindPathDis(SkillVisualInfo skill) { if (skill == null) return 0f; if (skill.UseNeedDis <= 1.5f) { return skill.UseNeedDis * 0.8f; } else { return skill.UseNeedDis - 1.5f; } } //是否可以选择玩家 public static bool CanShowRPhead(LocalPlayer user, RemotePlayer rp) { if (rp == null || user == null) return false; if (GameCenter.LuaSystem.Adaptor.IsTeamMember(rp.ID)) { return true; } //判断地图是否能够PK if (user.Scene.Cfg != null && user.Scene.Cfg.PkState == 0) { return true; } //判断是否在仇恨列表 if (user.IsStrikeBack(rp.ID)) { return false; } //var pkDefine = PkValueConstDefine.GetPkDefine(rp.PropMoudle.PkValue); //if (pkDefine != null && pkDefine.CanBeAttack) //PK值高到可以被主动攻击 //{ // return false; //} if (CheckPKModel(user, rp)) { return false; } return true; } //判断两个阵营之间是否可以攻击 public static bool MonsterSceneCampIsEnemy(int camp1, int camp2, int type) { switch (type) { case 1: if (camp2 >= 1 && camp2 <= 16) { return (camp1 >> (camp2 - 1) & 1) == 0; } break; } return camp1 != camp2; } //判断两个阵营之间是否可以攻击 public static bool PlayerSceneCampIsEnemy(int camp1, int camp2, int type) { switch (type) { case 1: return (camp1 >> 16) != (camp2 >> 16); } return camp1 != camp2; } //是否是技能的目标 public static bool CanAttackTarget(LocalPlayer user, Character c, bool checkBlock) { bool result = false; do { if (c == null || c.IsDead() || c.InSafeTile) { result = false; break; } //宠物不能被攻击 if (c is Pet) { result = false; break; } if (c is Monster) { //判断怪物阵营 result = MonsterSceneCampIsEnemy(user.PropMoudle.SceneCampID, c.PropMoudle.SceneCampID, user.Scene.Cfg.SceneCameMatchType); break; } if (c is RemotePlayer) { RemotePlayer rp = c as RemotePlayer; if (GameCenter.LuaSystem.Adaptor.IsTeamMember(rp.ID)) { result = false; break; } //判断地图是否能够PK if (user.Scene.Cfg != null && user.Scene.Cfg.PkState == 0) { result = false; break; } //判断是否在仇恨列表 if (user.IsStrikeBack(rp.ID)) { // 通过PK模式判断不能攻击的话,如果没有开启自动反击,那么就直接设置为false; result = CheckPKModel(user, rp); if (!result && GameCenter.GameSetting.GetSetting(GameSettingKeyCode.MandateAutoStrikeBack) <= 0) { result = false; } else { result = true; break; } } //最后检查PK模式 result = CheckPKModel(user, rp); } } while (false); if (result && c is RemotePlayer) { var rp = c as RemotePlayer; //检查变身状态 if (user.IsChangeModel) { //主角在变身状态 if (rp.IsChangeModel) { //其他玩家在变身状态 result = user.ChangeModelCfg.IsAttChange != 0 && rp.ChangeModelCfg.IsChangeAtt != 0; } else { //其他玩家不在变身状态 result = user.ChangeModelCfg.IsAttRole != 0; } } else { //主角不在变身状态 if (rp.IsChangeModel) { //其他玩家在变身状态 result = rp.ChangeModelCfg.IsRoleAtt != 0; } } } //检测阻挡,只有攻击其他玩家会检测阻挡,防止怪物穿墙打不到 if (checkBlock && result && c is RemotePlayer) { //判断攻击路径上是否有阻挡 var start = user.Position2d; var end = c.Position2d; var dis = Vector2.Distance(end, start); var whileCount = (int)(dis / user.Scene.navigator.CellSize + 1); var tmpPos = Vector2.zero; var lerpValue = 0f; for (int i = 0; i <= whileCount; ++i) { lerpValue = i / (float)whileCount; tmpPos = Vector2.Lerp(start, end, lerpValue); if (user.Scene.navigator.IsBlocked(tmpPos)) { result = false; break; } } } return result; } //通过PK模式来判断是否攻击 public static bool CheckPKModel(LocalPlayer user, RemotePlayer rp) { bool result = false; //判断PK模式 switch (user.PropMoudle.PkModel) { //和平攻击模式,不能攻击 case PKMode.PeaceMode: result = false; break; //全体攻击模式,可以攻击队友以外的 , 这里没有判断是否是队友,是因为如果是队友肯定不能被攻击,这个在攻击判断的最前面已经做了判断.IsTeamMember() case PKMode.AllMode: result = true; break; //本服模式 case PKMode.SelfServer: result = user.PropMoudle.ServerID != rp.PropMoudle.ServerID; break; //场景阵营模式,可以攻击对立场景阵营的玩家 case PKMode.SceneCampMode: result = PlayerSceneCampIsEnemy(user.PropMoudle.SceneCampID, rp.PropMoudle.SceneCampID, user.Scene.Cfg.SceneCameMatchType); break; // 公会:玩家可以攻击除公会外的其他玩家 case PKMode.GuildMode: if (user.GuildID <= 0) { result = true; } else { result = user.GuildID != rp.GuildID; } break; } return result; } //查找技能目标,根据技能方向来决定选择哪些目标 public static List<Character> MatchSkillTarget(LocalPlayer user, Vector2 userPos, Vector2 userDir, FindTargetInfo findInfo) { var scene = GameCenter.GameSceneSystem.GetActivedScene(); if (scene == null) { return null; } userDir = userDir.normalized; _searchCache.Clear(); var selectTarget = user.GetCurSelectedTarget(); switch (findInfo.AreaType) { case SkillTargetArea.Rectangle: //矩形 { float width = findInfo.RectWidth + 0.2f; float height = findInfo.RectHeight + 0.2f; List<Character> toBeSelectList = scene.FindAll<Character>(); if (toBeSelectList == null || toBeSelectList.Count <= 0) return null; var skillAreaShape = new Math2d.Obb(userPos, userDir, width / 2, height, 0); for (int i = 0; i < toBeSelectList.Count; ++i) { var c = toBeSelectList[i]; var tarCircle = new Math2d.Circle(c.Position2d, c.PropMoudle.LogicBodyRadius); if (Math2d.CollisionTest(skillAreaShape, tarCircle)) { if (CanAttackTarget(user, c, true)) { float disSq = (selectTarget == c) ? 0f : (userPos - c.Position2d).sqrMagnitude; _searchCache.Add(new SearchTargetInfo(c, disSq)); } } } } break; case SkillTargetArea.FanShaped: //扇形 { int angle = (int)findInfo.SectorAngle; float radius = findInfo.SectorRadius + 0.2f; List<Character> toBeSelectList = scene.FindAll<Character>(); if (toBeSelectList == null || toBeSelectList.Count <= 0) return null; var skillAreaShape = new Math2d.Sector(userPos, radius, userDir, angle * Mathf.Deg2Rad * 0.5f); for (int i = 0; i < toBeSelectList.Count; ++i) { var c = toBeSelectList[i]; var tarCircle = new Math2d.Circle(c.Position2d, c.PropMoudle.LogicBodyRadius); if (Math2d.CollisionTest(ref skillAreaShape, ref tarCircle)) { if (CanAttackTarget(user, c, true)) { float disSq = (selectTarget == c) ? 0f : (userPos - c.Position2d).sqrMagnitude; _searchCache.Add(new SearchTargetInfo(c, disSq)); } } } } break; case SkillTargetArea.Round: //圆形 { float radius = findInfo.RoundRadius + 0.2f; List<Character> toBeSelectList = scene.FindAll<Character>(); if (toBeSelectList == null || toBeSelectList.Count <= 0) return null; var skillCircle = new Math2d.Circle(userPos, radius + user.PropMoudle.LogicBodyRadius); for (int i = 0; i < toBeSelectList.Count; ++i) { var c = toBeSelectList[i]; var tarCircle = new Math2d.Circle(c.Position2d, c.PropMoudle.LogicBodyRadius); if (Math2d.CollisionTest(ref skillCircle, ref tarCircle)) { if (CanAttackTarget(user, c, true)) { float disSq = (selectTarget == c) ? 0f : (userPos - c.Position2d).sqrMagnitude; _searchCache.Add(new SearchTargetInfo(c, disSq)); } } } } break; } if (_searchCache.Count > 0) { _searchCache.Sort(compareFunc); List<Character> result = new List<Character>(); for (int i = 0; result.Count < findInfo.MaxTargetCount && i < _searchCache.Count; ++i) { if (!result.Contains(_searchCache[i].target)) { result.Add(_searchCache[i].target); } } _searchCache.Clear(); return result; } return null; } public static Character FindSkillMainTarget(LocalPlayer user, Vector2 findDir, float dis) { List<Character> toBeSelectList = GameCenter.GameSceneSystem.ActivedScene.FindAll<Character>(); _searchCache.Clear(); List<SearchTargetInfo> playerCache = new List<SearchTargetInfo>(32); for (int i = 0; i < toBeSelectList.Count && _searchCache.Count < 32; ++i) { Character c = toBeSelectList[i]; if (CanAttackTarget(user, c, false) && c.CanBeSelect && CheckDistanceValid(user, c, dis)) { var serachInfo = new SearchTargetInfo(c, user.GetSqrDistance2d(c.Position2d), (int)Vector2.Angle(findDir.normalized, (c.Position2d - user.Position2d).normalized)); _searchCache.Add(serachInfo); if (c is RemotePlayer) { playerCache.Add(serachInfo); } } } if (playerCache.Count > 0) { playerCache.Sort(compareAngleFunc); return playerCache[0].target; } if (_searchCache.Count > 0) { _searchCache.Sort(compareAngleFunc); return _searchCache[0].target; } return null; } public static Character FindCanAtkRecentlyTarget(LocalPlayer user) { List<Character> toBeSelectList = GameCenter.GameSceneSystem.ActivedScene.FindAll<Character>(); _searchCache.Clear(); for (int i = 0; i < toBeSelectList.Count && _searchCache.Count < 32; ++i) { Character c = toBeSelectList[i]; if (CanAttackTarget(user, c, false) && c.CanBeSelect) { float disSq = user.GetSqrDistance2d(c.Position2d); _searchCache.Add(new SearchTargetInfo(c, disSq)); } } if (_searchCache.Count > 0) { _searchCache.Sort(compareFunc); return _searchCache[0].target; } return null; } //查找挂机攻击目标 public static Character FindMandateTarget(LocalPlayer user, int targetID) { if (GameCenter.GameSceneSystem.ActivedScene == null) { return null; } if (targetID != 0) { List<Monster> toBeSelectList = GameCenter.GameSceneSystem.ActivedScene.FindAll<Monster>(); _searchCache.Clear(); for (int i = 0; i < toBeSelectList.Count && _searchCache.Count < 64; ++i) { Monster c = toBeSelectList[i]; if (c.PropMoudle.CfgID == targetID && CanAttackTarget(user, c, false) && c.CanBeSelect) { float disSq = user.GetSqrDistance2d(c.Position2d); _searchCache.Add(new SearchTargetInfo(c, disSq)); } } if (_searchCache.Count > 0) { _searchCache.Sort(compareFunc); return _searchCache[0].target; } } else { List<Character> toBeSelectList = GameCenter.GameSceneSystem.ActivedScene.FindAll<Character>(); _searchCache.Clear(); for (int i = 0; i < toBeSelectList.Count && _searchCache.Count < 64; ++i) { Character c = toBeSelectList[i]; if (CanAttackTarget(user, c, false) && c.CanBeSelect) { float disSq = user.GetSqrDistance2d(c.Position2d); var serchInfo = new SearchTargetInfo(c, disSq); _searchCache.Add(serchInfo); } } if (_searchCache.Count > 0) { _searchCache.Sort(compareFunc); return _searchCache[0].target; } } return null; } public static Collection FindCollectTarget(LocalPlayer user, int collectID) { List<Collection> toBeSelectList = GameCenter.GameSceneSystem.ActivedScene.FindAll<Collection>(); _searchCollectCache.Clear(); for (int i = 0; i < toBeSelectList.Count; ++i) { Collection c = toBeSelectList[i]; if (c.PropMoudle.CfgID == collectID) { float disSq = user.GetSqrDistance2d(c.Position2d); _searchCollectCache.Add(new SearchCollectionInfo(c, disSq)); } } if (_searchCollectCache.Count > 0) { _searchCollectCache.Sort(compareCollectFunc); } return _searchCollectCache.Count > 0 ? _searchCollectCache[0].target : null; } public static bool CheckDistanceValid(Character user, Character target, float length) { bool result = false; if (user != null && target != null && user.ModelTransform != null && target.ModelTransform != null) { var distance = (user.GetDistance2d(target.Position2d)) - target.PropMoudle.LogicBodyRadius / 2f; if (distance <= 0f) return true; Vector2 hitPos; if (distance <= length) { result = true; } } return result; } public static bool CheckMandeteDistanceValid(Character user, Character target, float length) { if (length > 6) { length = 6; } bool result = false; if (user != null && target != null && user.ModelTransform != null && target.ModelTransform != null) { var distance = (user.GetDistance2d(target.Position2d));// - target.PropMoudle.LogicBodyRadius / 2f; if (distance <= 0f) return true; Vector2 hitPos; if (distance < length) { result = true; } } return result; } //是否是技能的目标 public static bool CanShowWarningFiled(LocalPlayer user, Character target) { if (user == null) return false; if (target == null || target.IsDead()) { return false; } if (target is Monster) { //判断怪物阵营 return MonsterSceneCampIsEnemy(user.PropMoudle.SceneCampID, target.PropMoudle.SceneCampID, user.Scene.Cfg.SceneCameMatchType); } //宠物 RemotePlayer rp = null; if (target is Pet) { var pet = target as Pet; rp = GameCenter.GameSceneSystem.FindEntity<RemotePlayer>(pet.PropMoudle.MasterID); } if (target is RemotePlayer) { rp = target as RemotePlayer; } if (rp != null) { if (GameCenter.LuaSystem.Adaptor.IsTeamMember(rp.ID)) { return false; } //判断地图是否能够PK if (user.Scene.Cfg != null && user.Scene.Cfg.PkState == 0) { return false; } //判断是否在仇恨列表 if (user.IsStrikeBack(rp.ID)) { return true; } //最后判断PK模式 return CheckPKModel(user, rp); } return false; } //展示警示圈 public static void ShowWarningFiled(SkillVisualInfo cfg, Entity owner) { if (owner is Character && !CanShowWarningFiled(GameCenter.GameSceneSystem.GetLocalPlayer(), owner as Character)) { //只有敌对角色才会展示警示圈 return; } //展示警示圈 var findEvents = cfg.FindEvent(SkillEventDefine.PlayHit); if (findEvents.Count > 0) { var findEvent = findEvents[0] as PlayHitEventInfo; if (findEvent.FindInfo.ShowWarningField) { var hitTime = findEvent.EventFrame * Skill.OneFrameTime; switch (findEvent.FindInfo.AreaType) { case SkillTargetArea.Rectangle: GameCenter.WarningFiledManager.AddFiled(owner, false, false, findEvent.FindInfo.AreaType, findEvent.FindInfo.RectWidth, findEvent.FindInfo.RectHeight, findEvent.FindInfo.StartDis, hitTime); break; case SkillTargetArea.FanShaped: GameCenter.WarningFiledManager.AddFiled(owner, false, false, findEvent.FindInfo.AreaType, findEvent.FindInfo.SectorAngle, findEvent.FindInfo.SectorRadius, findEvent.FindInfo.StartDis, hitTime); break; case SkillTargetArea.Round: GameCenter.WarningFiledManager.AddFiled(owner, false, false, findEvent.FindInfo.AreaType, findEvent.FindInfo.RoundRadius, 0f, findEvent.FindInfo.StartDis, hitTime); break; } GameCenter.PushFixEvent(LogicEventDefine.EID_EVENT_SHOWBOSS_WARNINGFILED, new object[] { owner.Position2d, owner.GetFacingDirection2d(), hitTime, findEvent }); } } } //展示boss技能提示 public static void ShowBossSkillWarning(DeclareSkill cfg, Entity owner) { var monster = owner as Monster; if (monster == null) return; if (string.IsNullOrEmpty(cfg.PromptText) || cfg.PromptLifeTime <= 0) return; GameCenter.PushFixEvent(LogicEventDefine.EID_EVENT_SHOWSKILLWARNING_EFFECT, new object[] { cfg.PromptText, cfg.PromptDelay / 1000f, cfg.PromptLifeTime / 1000f }); } #region //私有类以及结构 struct SearchTargetInfo { public Character target; public float distanceSq; public int angle; public SearchTargetInfo(Character target, float distanceSq, int angle = 0) { this.target = target; this.distanceSq = distanceSq; this.angle = angle; } } //查询的采集物信息 private struct SearchCollectionInfo { public Collection target; public float distanceSq; public SearchCollectionInfo(Collection target, float distanceSq) { this.target = target; this.distanceSq = distanceSq; } } #endregion } }