feat(战斗系统): 实现基于技能距离的智能移动和攻击逻辑

重构英雄和怪物移动系统,引入技能距离缓存机制
在HeroAttrsComp中添加技能距离缓存管理
修改HeroSkillsComp以支持技能距离计算
更新移动系统使用技能距离判断攻击时机和停止位置
调整怪物配置统一使用水球技能
This commit is contained in:
2025-11-03 22:59:56 +08:00
parent 914ab0e8b9
commit 8152523e10
9 changed files with 333 additions and 27 deletions

View File

@@ -162,7 +162,7 @@ export const SkillSet: Record<number, SkillConfig> = {
},
6005: {
uuid:6005,name:"水球",sp_name:"m_water_ball_1",icon:"3039",TGroup:TGroup.Enemy,SType:SType.damage,act:"atk",DTType:DTType.single,DType:DType.MAGE,
ap:100,cd:5,t_num:1,hit_num:1,hit:2,hitcd:0.3,speed:720,cost:20,with:90,dis:360,ready:8001,EAnm:0,DAnm:9001,RType:RType.linear,EType:EType.collision,
ap:100,cd:5,t_num:1,hit_num:1,hit:2,hitcd:0.3,speed:720,cost:0,with:90,dis:360,ready:8001,EAnm:0,DAnm:9001,RType:RType.linear,EType:EType.collision,
buffs:[],neAttrs:[],info:"召唤大火球攻击前方所有敌人,造成300%攻击的伤害,有一定几率施加灼烧",
},

View File

@@ -138,61 +138,61 @@ export const HeroInfo: Record<number, heroInfo> = {
//怪物
5201:{uuid:5201,name:"兽人战士",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:30,mp:100,map:10,def:5,mdef:0,ap:5,dis:90,speed:100,skills:[6001],
type:HType.warrior,lv:1,hp:30,mp:100,map:10,def:5,mdef:0,ap:5,dis:90,speed:100,skills:[6005],
buff:[],tal:[],info:"普通怪物-战士型"},
5202:{uuid:5202,name:"兽人刺客",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.remote,lv:1,hp:20,mp:100,map:10,def:5,mdef:0,ap:5,dis:350,speed:150,skills:[6001],
type:HType.remote,lv:1,hp:20,mp:100,map:10,def:5,mdef:0,ap:5,dis:90,speed:150,skills:[6005],
buff:[],tal:[],info:"普通怪物-战士型"},
5203:{uuid:5203,name:"兽人护卫",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:60,mp:100,map:10,def:5,mdef:0,ap:5,dis:90,speed:100,skills:[6001],
type:HType.warrior,lv:1,hp:60,mp:100,map:10,def:5,mdef:0,ap:5,dis:90,speed:100,skills:[6005],
buff:[],tal:[],info:"普通怪物-战士型"},
// 1. 基础近战型
5204:{uuid:5204,name:"蝙蝠",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:28,mp:100,map:10,def:2,mdef:0,ap:6,dis:90,speed:125,skills:[6001],
type:HType.warrior,lv:1,hp:28,mp:100,map:10,def:2,mdef:0,ap:6,dis:90,speed:125,skills:[6005],
buff:[],tal:[],info:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
5205:{uuid:5205,name:"骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:35,mp:100,map:10,def:3,mdef:0,ap:7,dis:90,speed:120,skills:[6001],
type:HType.warrior,lv:1,hp:35,mp:100,map:10,def:3,mdef:0,ap:7,dis:90,speed:120,skills:[6005],
buff:[],tal:[],info:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
// 2. 快速突击型
5206:{uuid:5206,name:"石像鬼",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.assassin,lv:1,hp:26,mp:100,map:10,def:3,mdef:0,ap:8,dis:120,speed:180,skills:[6001],
type:HType.assassin,lv:1,hp:26,mp:100,map:10,def:3,mdef:0,ap:8,dis:80,speed:180,skills:[6005],
buff:[],tal:[],info:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
5207:{uuid:5207,name:"快速骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.assassin,lv:1,hp:22,mp:100,map:10,def:2,mdef:0,ap:7,dis:120,speed:200,skills:[6001],
type:HType.assassin,lv:1,hp:22,mp:100,map:10,def:2,mdef:0,ap:7,dis:80,speed:200,skills:[6005],
buff:[],tal:[],info:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
// 3. 重型坦克型
5208:{uuid:5208,name:"大型骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:140,mp:100,map:10,def:10,mdef:0,ap:10,dis:90,speed:85,skills:[6001],
type:HType.warrior,lv:1,hp:140,mp:100,map:10,def:10,mdef:0,ap:10,dis:90,speed:85,skills:[6005],
buff:[],tal:[],info:"重型坦克型:缓慢逼近,高血量,中等伤害"},
5209:{uuid:5209,name:"树人",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:160,mp:100,map:10,def:12,mdef:0,ap:12,dis:90,speed:80,skills:[6001],
type:HType.warrior,lv:1,hp:160,mp:100,map:10,def:12,mdef:0,ap:12,dis:90,speed:80,skills:[6005],
buff:[],tal:[],info:"重型坦克型:缓慢逼近,高血量,中等伤害"},
// 4. 远程骚扰型
5210:{uuid:5210,name:"骷髅弓手",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.remote,lv:1,hp:60,mp:100,map:8,def:4,mdef:0,ap:12,dis:450,speed:110,skills:[6005],
type:HType.remote,lv:1,hp:60,mp:100,map:8,def:4,mdef:0,ap:12,dis:80,speed:110,skills:[6005],
buff:[],tal:[],info:"远程骚扰型:保持距离发射箭矢,逼迫玩家走位"},
5211:{uuid:5211,name:"法师骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.mage,lv:1,hp:55,mp:100,map:25,def:4,mdef:5,ap:10,dis:400,speed:105,skills:[6005],
type:HType.mage,lv:1,hp:55,mp:100,map:25,def:4,mdef:5,ap:10,dis:80,speed:105,skills:[6005],
buff:[],tal:[],info:"远程骚扰型:保持距离释放法术弹幕,逼迫玩家走位"},
// 5. 特殊机制型
5212:{uuid:5212,name:"炸弹骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.assassin,lv:1,hp:30,mp:100,map:10,def:3,mdef:0,ap:25,dis:100,speed:130,skills:[6001],
type:HType.assassin,lv:1,hp:30,mp:100,map:10,def:3,mdef:0,ap:25,dis:100,speed:130,skills:[6005],
buff:[],tal:[],info:"特殊机制:接近玩家后自爆造成高额伤害,需优先击杀"},
// 6. 精英/BOSS型
5213:{uuid:5213,name:"亡灵领主(精英)",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.warrior,lv:3,hp:200,mp:100,map:20,def:10,mdef:5,ap:20,dis:100,speed:110,skills:[6001],
type:HType.warrior,lv:3,hp:200,mp:100,map:20,def:10,mdef:5,ap:20,dis:100,speed:110,skills:[6005],
buff:[],tal:[],info:"精英/BOSS高血量与独特机制波次高潮与重要经验来源"},
// 5. 特殊机制扩展
@@ -204,13 +204,13 @@ export const HeroInfo: Record<number, heroInfo> = {
// 治疗者:为周围怪物回血(此处以提升治疗效果和生命回复为基础被动)
// Attrs.HEAL_EFFECT=5 (RATIO=1)Attrs.HP_REGEN=3 (VALUE=0)
5215:{uuid:5215,name:"祭司(治疗者)",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.support,lv:1,hp:100,mp:160,map:18,def:5,mdef:8,ap:6,dis:350,speed:105,skills:[6005],
type:HType.support,lv:1,hp:100,mp:160,map:18,def:5,mdef:8,ap:6,dis:90,speed:105,skills:[6005],
buff:[],tal:[],info:"特殊机制:为周围怪物提供治疗增益与持续回复"},
// 光环怪为周围怪物提供增益此处以Buff效果提升与移动速度提升为基础被动
// Attrs.BUFF_UP=60 (RATIO=1)Attrs.SPEED=63 (RATIO=1)
5216:{uuid:5216,name:"光环幽灵(光环怪)",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
type:HType.support,lv:1,hp:85,mp:140,map:15,def:4,mdef:7,ap:7,dis:350,speed:110,skills:[6005],
type:HType.support,lv:1,hp:85,mp:140,map:15,def:4,mdef:7,ap:7,dis:90,speed:110,skills:[6005],
buff:[],tal:[],info:"特殊机制:为周围怪物提供增益光环,加速与增益效果强化"},
};

View File

@@ -74,7 +74,7 @@ export class Hero extends ecs.Entity {
model.is_master = true;
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
skillsComp.initSkills(hero.skills,uuid);
skillsComp.initSkills(hero.skills, uuid, this);
// 设置基础属性
model.base_ap = hero.ap;

View File

@@ -4,6 +4,7 @@ import { smc } from "../common/SingletonModuleComp";
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { HeroInfo, AttrSet, HeroUpSet } from "../common/config/heroSet";
import { HeroSkillsComp } from "./HeroSkills";
@ecs.register('HeroAttrs')
@@ -33,6 +34,10 @@ export class HeroAttrsComp extends ecs.Comp {
Attrs: any = []; // 最终属性数组经过Buff计算后
NeAttrs: any = []; // 负面状态数组
// ==================== 技能距离缓存 ====================
maxSkillDistance: number = 0; // 最远技能攻击距离缓存受MP影响
minSkillDistance: number = 0; // 最近技能攻击距离缓存不受MP影响用于停止位置判断
// ==================== Buff/Debuff 系统 ====================
/** 持久型buff数组 - 不会自动过期 */
BUFFS: Record<number, Array<{value: number, BType: BType}>> = {};
@@ -347,6 +352,41 @@ export class HeroAttrsComp extends ecs.Comp {
return this.NeAttrs[NeAttrs.IN_FROST]?.time > 0;
}
// ==================== 技能距离缓存管理 ====================
/**
* 更新技能距离缓存
* 在技能初始化、新增技能、MP变化时调用
* @param skillsComp 技能组件
*/
public updateSkillDistanceCache(skillsComp: HeroSkillsComp): void {
if (!skillsComp) {
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
return;
}
// 最远距离使用当前MP可施放的技能
this.maxSkillDistance = skillsComp.getMaxSkillDistance(this.mp);
// 最近距离使用所有技能中的最小距离不考虑MP限制用于停止位置判断
this.minSkillDistance = skillsComp.getAbsoluteMinSkillDistance();
}
/**
* 获取缓存的最远技能攻击距离
* @returns 最远攻击距离
*/
public getCachedMaxSkillDistance(): number {
return this.maxSkillDistance;
}
/**
* 获取缓存的最近技能攻击距离
* @returns 最近攻击距离
*/
public getCachedMinSkillDistance(): number {
return this.minSkillDistance;
}
reset() {
@@ -370,6 +410,9 @@ export class HeroAttrsComp extends ecs.Comp {
this.NeAttrs = [];
this.BUFFS = {};
this.BUFFS_TEMP = {};
// 重置技能距离缓存
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
this.is_dead = false;
this.is_count_dead = false;
this.is_atking = false;
@@ -457,6 +500,9 @@ export class HeroAttrSystem extends ecs.ComblockSystem
// 1. 更新临时 Buff/Debuff时间递减过期自动移除
model.updateTemporaryBuffsDebuffs(this.dt);
// 记录MP变化前的值
const oldMp = model.mp;
if(this.timer.update(this.dt)){
// 2. HP/MP 自然回复(业务规则)
model.mp += HeroUpSet.MP
@@ -471,6 +517,14 @@ export class HeroAttrSystem extends ecs.ComblockSystem
model.hp = model.Attrs[Attrs.HP_MAX];
}
// 4. 如果MP发生变化更新最大技能距离缓存最小距离不受MP影响
if (model.mp !== oldMp) {
const skillsComp = e.get(HeroSkillsComp);
if (skillsComp) {
model.updateSkillDistanceCache(skillsComp);
}
}
// 每 60 帧输出一次统计
this.frameCount++;
if (this.frameCount % 60 === 0 && this.entityCount === 1) {

View File

@@ -1,6 +1,7 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroViewComp } from "./HeroViewComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroSkillsComp } from "./HeroSkills";
import { smc } from "../common/SingletonModuleComp";
import { FacSet } from "../common/config/GameSet";
import { HType } from "../common/config/heroSet";
@@ -30,7 +31,7 @@ export class HeroMoveComp extends ecs.Comp {
@ecs.register('HeroMoveSystem')
export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(HeroMoveComp, HeroViewComp, HeroAttrsComp);
return ecs.allOf(HeroMoveComp, HeroViewComp, HeroAttrsComp, HeroSkillsComp);
}
update(e: ecs.Entity) {
@@ -44,8 +45,10 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
if (model.fac !== FacSet.HERO) return;
if (!move.moving) return;
const shouldStop = this.checkEnemiesInFace(e);
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
const shouldStopInFace = this.checkEnemiesInFace(e);
const shouldStopAtMinRange = this.shouldStopAtMinSkillRange(e);
const shouldStop = shouldStopInFace || shouldStopAtMinRange;
model.is_atking = this.checkEnemiesInSkillRange(e);
// 更新渲染层级
this.updateRenderOrder(e);
@@ -236,6 +239,36 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
return found;
}
/** 检测技能攻击范围内敌人 */
private checkEnemiesInSkillRange(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
const heroAttrs = entity.get(HeroAttrsComp);
if (!currentView || !currentView.node || !heroAttrs) return false;
const currentPos = currentView.node.position;
const team = heroAttrs.fac;
// 使用缓存的最远技能攻击距离判断攻击时机
const maxSkillDistance = heroAttrs.getCachedMaxSkillDistance();
if (maxSkillDistance === 0) return false;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
if (distance <= maxSkillDistance) {
found = true;
return true;
}
}
});
return found;
}
/** 更新渲染层级 */
private updateRenderOrder(entity: ecs.Entity) {
const currentView = entity.get(HeroViewComp);
@@ -283,4 +316,31 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
});
}
/** 检查是否应该基于最近技能距离停止移动 */
private shouldStopAtMinSkillRange(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
const heroAttrs = entity.get(HeroAttrsComp);
if (!currentView || !currentView.node || !heroAttrs) return false;
const currentPos = currentView.node.position;
const team = heroAttrs.fac;
// 使用缓存的最近技能攻击距离
const minSkillDistance = heroAttrs.getCachedMinSkillDistance();
if (minSkillDistance === 0) return false;
// 检查是否有敌人在最近技能距离内
return ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
return distance <= minSkillDistance;
}
return false;
});
}
}

View File

@@ -14,6 +14,7 @@ export interface SkillSlot {
cd_max: number; // 最大CD时间
cost: number; // MP消耗
level: number; // 技能等级(预留)
dis: number; // 攻击距离
}
/**
@@ -41,8 +42,10 @@ export class HeroSkillsComp extends ecs.Comp {
/**
* 初始化技能列表
* @param sUuids 技能配置ID数组
* @param uuid 英雄UUID
* @param entity 实体对象(用于更新技能距离缓存)
*/
initSkills(sUuids: number[], uuid: number) {
initSkills(sUuids: number[], uuid: number, entity?: ecs.Entity) {
this.skills = [];
for (let i = 0; i < sUuids.length; i++) {
const s_uuid = sUuids[i];
@@ -59,14 +62,25 @@ export class HeroSkillsComp extends ecs.Comp {
cd_max: cdMax,
cost: config.cost,
level: 1,
dis: Number(config.dis),
};
}
// 更新技能距离缓存
if (entity) {
const attrsComp = entity.get(HeroAttrsComp);
if (attrsComp) {
attrsComp.updateSkillDistanceCache(this);
}
}
}
/**
* 添加单个技能
* @param s_uuid 技能配置ID
* @param entity 实体对象(用于更新技能距离缓存)
*/
addSkill(s_uuid: number) {
addSkill(s_uuid: number, entity?: ecs.Entity) {
const config = SkillSet[s_uuid];
if (!config) {
console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`);
@@ -77,8 +91,17 @@ export class HeroSkillsComp extends ecs.Comp {
cd: 0,
cd_max: config.cd,
cost: config.cost,
level: 1
level: 1,
dis: Number(config.dis),
};
// 更新技能距离缓存
if (entity) {
const attrsComp = entity.get(HeroAttrsComp);
if (attrsComp) {
attrsComp.updateSkillDistanceCache(this);
}
}
}
/**
@@ -156,6 +179,87 @@ export class HeroSkillsComp extends ecs.Comp {
return ready;
}
/**
* 检查技能攻击距离是否足够
* @param s_uuid 技能配置ID
* @param distance 目标距离
* @returns 是否在攻击范围内
*/
canReachTarget(s_uuid: number, distance: number): boolean {
const skill = this.getSkill(s_uuid);
if (!skill) {
return false;
}
return distance <= skill.dis;
}
/**
* 获取技能的攻击距离
* @param s_uuid 技能配置ID
* @returns 攻击距离如果技能不存在返回0
*/
getSkillDistance(s_uuid: number): number {
const skill = this.getSkill(s_uuid);
return skill ? skill.dis : 0;
}
/**
* 获取可施放技能中的最远攻击距离
* @param mp 当前MP值
* @returns 最远攻击距离如果没有可用技能返回0
*/
getMaxSkillDistance(mp: number): number {
const readySkills = this.getReadySkills(mp);
if (readySkills.length === 0) return 0;
let maxDistance = 0;
for (const s_uuid of readySkills) {
const skill = this.getSkill(s_uuid);
if (skill && skill.dis > maxDistance) {
maxDistance = skill.dis;
}
}
return maxDistance;
}
/**
* 获取可施放技能中的最近攻击距离
* @param mp 当前MP值
* @returns 最近攻击距离如果没有可用技能返回0
*/
getMinSkillDistance(mp: number): number {
const readySkills = this.getReadySkills(mp);
if (readySkills.length === 0) return 0;
let minDistance = Number.MAX_VALUE;
for (const s_uuid of readySkills) {
const skill = this.getSkill(s_uuid);
if (skill && skill.dis < minDistance) {
minDistance = skill.dis;
}
}
return minDistance === Number.MAX_VALUE ? 0 : minDistance;
}
/**
* 获取所有技能中的最小攻击距离不考虑MP限制
* 用于移动停止判断,让英雄在合适位置等待回蓝
* @returns 最小攻击距离如果没有技能返回0
*/
getAbsoluteMinSkillDistance(): number {
const skillIds = Object.keys(this.skills).map(Number);
if (skillIds.length === 0) return 0;
let minDistance = Number.MAX_VALUE;
for (const s_uuid of skillIds) {
const skill = this.getSkill(s_uuid);
if (skill && skill.dis < minDistance) {
minDistance = skill.dis;
}
}
return minDistance === Number.MAX_VALUE ? 0 : minDistance;
}
reset() {
this.skills = {};
}

View File

@@ -88,7 +88,7 @@ export class Monster extends ecs.Entity {
model.Attrs[Attrs.DIS] = hero.dis;
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
skillsComp.initSkills(hero.skills,uuid);
skillsComp.initSkills(hero.skills, uuid, this);
this.add(view);
oops.message.dispatchEvent("monster_load",this)

View File

@@ -1,6 +1,7 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroViewComp } from "./HeroViewComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroSkillsComp } from "./HeroSkills";
import { smc } from "../common/SingletonModuleComp";
import { FacSet, IndexSet } from "../common/config/GameSet";
import { Attrs } from "../common/config/HeroAttrs";
@@ -32,7 +33,7 @@ export class MonMoveComp extends ecs.Comp {
@ecs.register('MonMoveSystem')
export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp);
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp, HeroSkillsComp);
}
update(e: ecs.Entity) {
@@ -46,8 +47,10 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
if (model.fac !== FacSet.MON) return;
if (!move.moving) return;
const shouldStop = this.checkEnemiesInFace(e);
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
const shouldStopInFace = this.checkEnemiesInFace(e);
const shouldStopAtMinRange = this.shouldStopAtMinSkillRange(e);
const shouldStop = shouldStopInFace || shouldStopAtMinRange;
model.is_atking = this.checkEnemiesInSkillRange(e);
// 🔥 移除渲染层级更新:各线路固定,后召唤的天然层级更高,无需动态调整
@@ -110,6 +113,36 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
return found;
}
/** 检测技能攻击范围内敌人 */
private checkEnemiesInSkillRange(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
const heroAttrs = entity.get(HeroAttrsComp);
if (!currentView || !currentView.node || !heroAttrs) return false;
const currentPos = currentView.node.position;
const team = heroAttrs.fac;
// 使用缓存的最远技能攻击距离判断攻击时机
const maxSkillDistance = heroAttrs.getCachedMaxSkillDistance();
if (maxSkillDistance === 0) return false;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
if (distance <= maxSkillDistance) {
found = true;
return true;
}
}
});
return found;
}
/** 检测面前是否有敌人 */
private checkEnemiesInFace(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
@@ -132,4 +165,31 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
});
return found;
}
/** 检查是否应该基于最近技能距离停止移动 */
private shouldStopAtMinSkillRange(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
const heroAttrs = entity.get(HeroAttrsComp);
if (!currentView || !currentView.node || !heroAttrs) return false;
const currentPos = currentView.node.position;
const team = heroAttrs.fac;
// 使用缓存的最近技能攻击距离
const minSkillDistance = heroAttrs.getCachedMinSkillDistance();
if (minSkillDistance === 0) return false;
// 检查是否有敌人在最近技能距离内
return ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
return distance <= minSkillDistance;
}
return false;
});
}
}

View File

@@ -54,6 +54,9 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
const config = SkillSet[skill.s_uuid];
if (!config || config.SType !== SType.damage) continue;
// 检查是否有敌人在技能攻击范围内
if (!this.hasEnemyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
// ✅ 开始执行施法
this.startCast(e,skill);
@@ -276,6 +279,31 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
return allies;
}
/**
* 检查技能攻击范围内是否有敌人
*/
private hasEnemyInSkillRange(heroView: HeroViewComp, heroAttrs: HeroAttrsComp, skillDistance: number): boolean {
if (!heroView || !heroView.node) return false;
const currentPos = heroView.node.position;
const team = heroAttrs.fac;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
if (distance <= skillDistance) {
found = true;
return true;
}
}
});
return found;
}
}