Files
Main/Assets/Code/Logic/_Required/Entity/Character/Utils/CombatUtil.cs
2025-01-25 04:38:09 +08:00

786 lines
28 KiB
C#

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
}
}