//选择目标 List <ServerNPC> selectTarget(BulletHurtType HurtType, ServerNPC suffer, bool isFinal, WarServerNpcMgr npcMgr, float radius) { List <ServerNPC> target = new List <ServerNPC>(); switch (HurtType) { /// 伤害所有碰见的敌人 case BulletHurtType.Keep_Dmg: target.Add(suffer); break; /// 伤害最终的敌人 case BulletHurtType.Final_Target_Radius: if (isFinal) { /// --- 默认对敌人的各种类型友方产生攻击 --- /// IEnumerable <ServerNPC> itor = SelectorTools.GetNPCInRange(suffer, radius, npcMgr, KindOfNPC.Life, TargetClass.Friendly, NpcStatus.None); target.AddRange(itor); } break; /// 伤害最终的敌人 case BulletHurtType.Final_Target: if (isFinal) { target.Add(suffer); } break; } return(target); }
/// <summary> /// 这个Targets不能为空 /// </summary> /// <param name="caster">Caster.</param> /// <param name="targets">Targets.</param> /// <param name="efCfg">Ef cfg.</param> /// <param name="npcMgr">Npc mgr.</param> public List <ServerNPC> Select(ServerNPC caster, List <ServerNPC> targets, EffectConfigData efCfg, WarServerNpcMgr npcMgr) { #if DEBUG Utils.Assert(efCfg == null, "Can't find target unless EffectConfigData isn't null."); Utils.Assert(npcMgr == null, "Can't find target unless WarServerNpcMgr isn't null."); Utils.Assert(targets == null, "Can't find target unless targets isn't null."); #endif List <ServerNPC> reTarget = new List <ServerNPC>(); //转换为Camp类型,还有一部分的检查不在这里 //剔除自己的是在Factory里面(即EffectAreaSector) CAMP camp = efCfg.Flags.switchTo(caster.Camp); int count = targets.Count; if (count > 0) { for (int i = 0; i < count; ++i) { UVec3 anchor = targets[i].transform.position; reTarget.AddRange(SelectorTools.GetNPCRadius(anchor, caster.data.configData.radius + efCfg.eParam1, camp, npcMgr)); } } return(reTarget); }
/// <summary> /// 检测回复技能 /// /// 检测逻辑和上面的并不太一致 /// 判定顺序是: /// 0. 判定技能的类型 /// 1. 判定施法者的状态 /// 2. 选在施法的距离(特定范围)内血最少的 英雄 NPC /// </summary> /// <returns><c>true</c>, if cast was caned, <c>false</c> otherwise.</returns> /// <param name="caster">Caster.</param> public bool canCast(ServerNPC caster, RtSkData rtOne) { bool can = true; #if DEBUG Utils.Assert(caster == null, "Can't know whether can cast skill unless caster isn't null."); Utils.Assert(rtOne == null, "Can't know whether can cast skill unless runtime skill isn't null"); #endif SkillConfigData skillCfg = rtOne.skillCfg; /// 0. 判定技能的类型 can = skillCfg.DamageType == 1; if (can == false) { return(can); } /// 1.判定施法者的状态 //只有LifeNPC才检查施法者的状态 ServerLifeNpc castlife = caster as ServerLifeNpc; if (castlife != null) { if (skillCfg.CasterStatusReject.AnySame(castlife.curStatus)) { //不可施法 can = false; } } if (can == false) { return(can); } /// 2. 选在施法的距离(特定范围)内血最少的 英雄 NPC List <ServerNPC> friendlyArmy = SelectorTools.GetNpcWithInRange(caster, skillCfg.Distance, NpcMgr, caster.Camp, KindOfNPC.Life, LifeNPCType.Hero); if (friendlyArmy != null && friendlyArmy.Count > 0) { //选择血最少的 ServerNPC friend = friendlyArmy.OrderBy(npc => (npc.data.rtData.curHp * 1.0f / npc.data.rtData.totalHp)).FirstOrDefault(); if (friend != null) { NPCRuntimeData npcRtdt = friend.data.rtData; float factor = npcRtdt.curHp * 1.0f / npcRtdt.totalHp; //低于20%的 if (factor > 0.2f) { can = false; } } } return(can); }
void HandleDistance() { List <ServerNPC> outOfRange = new List <ServerNPC>(); List <int> Rmed = new List <int>(); ServerNPC castor = null; int cnt = checkList.Count; for (int i = 0; i < cnt; ++i) { Pair pair = checkList[i]; float radius = pair.castor.data.configData.radius + pair.target.data.configData.radius + cfg.Param1 * Consts.OneThousand; bool isIn = SelectorTools.IsInRange(pair.castor.transform.position, radius, pair.target.transform.position); if (isIn == false) { castor = pair.castor; outOfRange.Add(pair.target); Rmed.Add(i); } } if (outOfRange.Count > 0) { /// /// 做一个概率上的检测 /// IEnumerable <ServerNPC> itor = outOfRange.Where(n => PseudoRandom.getInstance().happen(cfg.Prob)); warMgr.triMgr.trigCastor.cast(castor, itor, cfg); } if (Rmed.Count > 0) { int count = Rmed.Count; for (int i = 0; i < count; ++i) { checkList.RemoveAt(Rmed[i]); } } }
/// <summary> /// 扇形选择区域 /// </summary> /// <param name="caster">Caster.</param> /// <param name="targets">Targets 不适用,,可以为Null</param> /// <param name="efCfg">Effect的配置</param> /// <param name="npcMgr">WarNpcManager</param> public List <ServerNPC> Select(ServerNPC caster, List <ServerNPC> targets, EffectConfigData efCfg, WarServerNpcMgr npcMgr) { #if DEBUG Utils.Assert(caster == null, "Can't find targets unless caster isn't null."); Utils.Assert(efCfg == null, "Can't find targets unless EffectConfigData isn't null."); Utils.Assert(npcMgr == null, "Can't find targets unless WarServerNpcMgr isn't null."); #endif //转换为Camp类型,还有一部分的检查不在这里 //剔除自己的是在Factory里面(即EffectAreaSector) CAMP camp = efCfg.Flags.switchTo(caster.Camp); Transform trans = caster.transform; float Y = trans.localPosition.y; //角度转化为弧度 float angel = Mathf.Deg2Rad * efCfg.eParam2 * 0.5f; float radius = efCfg.eParam1 + caster.data.configData.radius; float X = Mathf.Sin(angel) * radius; float Z = Mathf.Cos(angel) * radius; //本地坐标 UVec3[] localVecs = new UVec3[4]; localVecs[0] = new UVec3() { x = 0F, y = Y, z = 0F, }; localVecs[1] = new UVec3() { x = -X, y = Y, z = Z, }; localVecs[2] = new UVec3() { x = 0, y = Y, z = radius, }; localVecs[3] = new UVec3() { x = X, y = Y, z = Z, }; //世界坐标 UVec3[] worldVecs = new UVec3[4]; worldVecs[0] = trans.TransformPoint(localVecs[0]); worldVecs[1] = trans.TransformPoint(localVecs[1]); worldVecs[2] = trans.TransformPoint(localVecs[2]); worldVecs[3] = trans.TransformPoint(localVecs[3]); //转换为2D坐标 PointF[] Vecs = new PointF[4]; Vecs[0] = new PointF() { X = worldVecs[0].x, Y = worldVecs[0].z }; Vecs[1] = new PointF() { X = worldVecs[1].x, Y = worldVecs[1].z }; Vecs[2] = new PointF() { X = worldVecs[2].x, Y = worldVecs[2].z }; Vecs[3] = new PointF() { X = worldVecs[3].x, Y = worldVecs[3].z }; Polygon polygon = new Polygon(Vecs); List <ServerNPC> TargetList = SelectorTools.GetNPCPolygon(polygon, camp, npcMgr).ToList(); return(TargetList); }
/// <param name="targets">没用的</param> public List <ServerNPC> Select(ServerNPC caster, List <ServerNPC> targets, EffectConfigData efCfg, WarServerNpcMgr npcMgr) { #if DEBUG Utils.Assert(caster == null, "Can't find target unless caster isn't null."); Utils.Assert(efCfg == null, "Can't find target unless EffectConfigData isn't null."); Utils.Assert(npcMgr == null, "Can't find target unless WarServerNpcMgr isn't null."); #endif //转换为Camp类型,还有一部分的检查不在这里 //剔除自己的是在Factory里面(即EffectAreaSector) CAMP camp = efCfg.Flags.switchTo(caster.Camp); UTran trans = caster.transform; float Y = trans.localPosition.y; float halfWidth = efCfg.eParam2 * 0.5F + caster.data.configData.radius; float Height = efCfg.eParam1 + caster.data.configData.radius; UVec3[] localVecs = new UVec3[4]; localVecs[0] = new UVec3(-halfWidth, Y, 0f); localVecs[1] = new UVec3(-halfWidth, Y, Height); localVecs[2] = new UVec3(halfWidth, Y, Height); localVecs[3] = new UVec3(halfWidth, Y, 0f); UVec3[] WorldVecs = new UVec3[4]; WorldVecs[0] = trans.TransformPoint(localVecs[0]); WorldVecs[1] = trans.TransformPoint(localVecs[1]); WorldVecs[2] = trans.TransformPoint(localVecs[2]); WorldVecs[3] = trans.TransformPoint(localVecs[3]); //caster.transform.forward; PointF[] vecs = new PointF[4]; vecs[0] = new PointF() { X = WorldVecs[0].x, Y = WorldVecs[0].z, }; vecs[1] = new PointF() { X = WorldVecs[1].x, Y = WorldVecs[1].z, }; vecs[2] = new PointF() { X = WorldVecs[2].x, Y = WorldVecs[2].z, }; vecs[3] = new PointF() { X = WorldVecs[3].x, Y = WorldVecs[3].z, }; Polygon rectangle = new Polygon(vecs); List <ServerNPC> TargetList = SelectorTools.GetNPCPolygon(rectangle, camp, npcMgr).ToList(); return(TargetList); }
/// <summary> /// 选择的规则是: /// 多目标判定规则 /// 1. 判定施法者的状态 /// 2. 判定施法者的上次目标,如果目标在攻击范围内,则选择为最优先目标 /// 3. 判定施法的范围类型 /// 4. 目标的状态 /// 5. 根据施法的距离选择出目标 /// 6. 根据血量选择出目标 /// 7. 目标的优先级 /// 8. 单体攻击敌方,并且有最高优先级的目标 /// /// /// 单目标判定规则 /// /// /// /// </summary> /// <returns><c>true</c>, if cast was caned, <c>false</c> otherwise.</returns> public IEnumerable <ServerNPC> Select(ServerNPC caster, RtSkData rtOne, Sight sight) { //枚举器 IEnumerable <ServerNPC> itor = null, itor1 = null; #if DEBUG Utils.Assert(caster == null, "Can't find target unless caster isn't null"); Utils.Assert(rtOne == null, "Can't find target unless runtime skill isn't null"); #endif SkillConfigData skillCfg = rtOne.skillCfg; /// 视野范围 -- 决定索敌范围 float CheckDistance = 0F; if (sight == Sight.NearSight) { CheckDistance = skillCfg.Distance; } else { CheckDistance = caster.data.rtData.seekRange; } bool castcan = true; //只有LifeNPC才检查施法者的状态 //1. 判定施法者的状态 ServerLifeNpc castlife = caster as ServerLifeNpc; if (castlife != null) { if (castlife.curStatus.AnySame(skillCfg.CasterStatusReject)) { //不可施法 castcan = false; } } if (castcan == false) { return(new List <ServerNPC>().AsEnumerable <ServerNPC>()); } //能进入CD rtOne.EnterCD(); /// /// 3. 判定施法的范围类型 . /// 单体和AOE都需要在技能配置里选择出目标 , 再次到Effect里面选择目标。 -- 叫做前置判定 /// 方向性则不需要选择目标,该类型的技能可对空地释放,再次到EffectTarget的目标是skill的目标 -- 叫做后置判定 /// TargetClass rtTargetCls = skillCfg.SkillTarget; if (skillCfg.RangeType == RangeClass.Direction) { //后置判定 return(new List <ServerNPC>().AsEnumerable <ServerNPC>()); } bool isMultiTarget = skillCfg.RangeType != RangeClass.SingleTarget; bool isNoPriority = skillCfg.TargetPriority == SkTargetPriority.NonePriority; bool isTargetSelf = rtTargetCls.AnySame(TargetClass.Self); //单目标 ServerNPC singlePriority = null; LifeNPCType type = skillCfg.TargetType.toPositive(); /// /// 是否为多目标的战斗, 或者是没有优先级的单目标 /// if (isMultiTarget || isNoPriority || isTargetSelf) { /// /// 4. 目标的状态 /// if (rtTargetCls.AnySame(TargetClass.Friendly)) { //友方 itor1 = SelectorTools.GetNPCValideStatus(caster, npcMgr, KindOfNPC.Life, rtTargetCls, NpcStatus.None, type); } else if (rtTargetCls.AnySame(TargetClass.Hostile)) { //敌方 itor1 = SelectorTools.GetNPCValideStatus(caster, npcMgr, KindOfNPC.Life, rtTargetCls, skillCfg.TargetStatusReject, type); } else if (rtTargetCls.AnySame(TargetClass.Self)) { //自己 List <ServerNPC> targets = new List <ServerNPC>(); targets.Add(caster); itor = targets.AsEnumerable <ServerNPC>(); return(targets); } else { itor1 = SelectorTools.GetNPCValideStatus(caster, npcMgr, KindOfNPC.Life, rtTargetCls, skillCfg.TargetStatusReject, type); ConsoleEx.DebugLog("Warning : We should have a Camp. But sometimes, it's Ok.", ConsoleEx.RED); } /// /// 5. 根据施法的距离选择出目标 /// itor1 = SelectorTools.GetNPCInRadius(caster, CheckDistance, rtTargetCls, itor1); /// /// 6. 根据血量选择出目标 /// itor1 = SelectorTools.GetNPCByHp(caster, rtTargetCls, itor1); if (isNoPriority) { singlePriority = itor1.FirstOrDefault(); } } else { /// /// 获取单目标 /// singlePriority = SelectorTools.GetPrioritiedNpc(caster, rtTargetCls, npcMgr, CheckDistance, skillCfg.TargetStatusReject, skillCfg.TargetPriority, priorityMgr, type); //单目标的友方,有可能选择到自己(但并不属于 TargetClass 64.就针对自己) if (!rtTargetCls.AnySame(TargetClass.Hostile)) { List <ServerNPC> broker = new List <ServerNPC>(); if (singlePriority != null) { broker.Add(singlePriority); } itor1 = broker.AsEnumerable <ServerNPC>(); } } if (isMultiTarget == false) { /// /// 9. 单体攻击敌方,只有打敌方单体的时候,TargetID才会被重新设定 /// if (rtTargetCls.AnySame(TargetClass.Hostile)) { ServerLifeNpc HighestPriority = null; /// /// 检测嘲讽 /// int tarId = caster.getHighestHatred; if (tarId != -1) { //有被嘲讽的目标 HighestPriority = npcMgr.GetNPCByUniqueID(tarId) as ServerLifeNpc; } else { //没有被嘲讽的目标 /// /// 判定施法者的上次目标,则选择为最优先目标 /// 此逻辑,能保证追击残血逃跑的 /// if (caster.TargetID != -1) { HighestPriority = npcMgr.GetNPCByUniqueID(caster.TargetID) as ServerLifeNpc; if (HighestPriority != null) { /// /// 如果单一目标的是英雄, /// if (singlePriority != null && singlePriority.data.configData.type == LifeNPCType.Hero && HighestPriority.data.configData.type != LifeNPCType.Hero) { HighestPriority = null; caster.TargetID = -1; } else { bool validate1 = true, validate2 = true, validate3 = true; /// 生命判定 if (HighestPriority.data.rtData.curHp <= 0) { validate1 = false; } /// 距离判定 float distance = SelectorTools.GetDistance(HighestPriority.transform.position, caster.transform.position); distance = distance - HighestPriority.data.configData.radius - caster.data.configData.radius; /// 视野范围 if (distance > CheckDistance) { validate2 = false; } ///如果有相同的状态,则不让释放技能 validate3 = !HighestPriority.curStatus.AnySame(skillCfg.TargetStatusReject); /// 失败的判定 bool validate = validate1 & validate2 & validate3; if (!validate) { HighestPriority = null; caster.TargetID = -1; } else { caster.RstTimeout(); } } } else { caster.TargetID = -1; } } } //找到普通选择出的目标 if (HighestPriority == null) { ServerNPC target = singlePriority; caster.TargetID = target != null ? target.UniqueID : -1; HighestPriority = target as ServerLifeNpc; } List <ServerNPC> high = new List <ServerNPC>(); if (HighestPriority != null) { high.Add(HighestPriority); } itor1 = high.AsEnumerable <ServerNPC>(); } else { /// /// 10.单体友方 /// if (itor1.Any()) { itor1 = itor1.Take(1); } } } itor = itor1; return(itor); }