feat(战斗系统): 实现基于技能距离的智能移动和攻击逻辑
重构英雄和怪物移动系统,引入技能距离缓存机制 在HeroAttrsComp中添加技能距离缓存管理 修改HeroSkillsComp以支持技能距离计算 更新移动系统使用技能距离判断攻击时机和停止位置 调整怪物配置统一使用水球技能
This commit is contained in:
@@ -162,7 +162,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
|||||||
},
|
},
|
||||||
6005: {
|
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,
|
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%攻击的伤害,有一定几率施加灼烧",
|
buffs:[],neAttrs:[],info:"召唤大火球攻击前方所有敌人,造成300%攻击的伤害,有一定几率施加灼烧",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -138,61 +138,61 @@ export const HeroInfo: Record<number, heroInfo> = {
|
|||||||
|
|
||||||
//怪物
|
//怪物
|
||||||
5201:{uuid:5201,name:"兽人战士",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"普通怪物-战士型"},
|
buff:[],tal:[],info:"普通怪物-战士型"},
|
||||||
|
|
||||||
5202:{uuid:5202,name:"兽人刺客",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"普通怪物-战士型"},
|
buff:[],tal:[],info:"普通怪物-战士型"},
|
||||||
|
|
||||||
5203:{uuid:5203,name:"兽人护卫",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"普通怪物-战士型"},
|
buff:[],tal:[],info:"普通怪物-战士型"},
|
||||||
|
|
||||||
// 1. 基础近战型
|
// 1. 基础近战型
|
||||||
5204:{uuid:5204,name:"蝙蝠",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
|
buff:[],tal:[],info:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
|
||||||
|
|
||||||
5205:{uuid:5205,name:"骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
|
buff:[],tal:[],info:"基础近战型:直接向玩家移动,接触造成伤害;中速、低血、数量多"},
|
||||||
|
|
||||||
// 2. 快速突击型
|
// 2. 快速突击型
|
||||||
5206:{uuid:5206,name:"石像鬼",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
|
buff:[],tal:[],info:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
|
||||||
|
|
||||||
5207:{uuid:5207,name:"快速骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
|
buff:[],tal:[],info:"快速突击型:高速直线冲锋,接触伤害;高速、低血、成群出现"},
|
||||||
|
|
||||||
// 3. 重型坦克型
|
// 3. 重型坦克型
|
||||||
5208:{uuid:5208,name:"大型骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"重型坦克型:缓慢逼近,高血量,中等伤害"},
|
buff:[],tal:[],info:"重型坦克型:缓慢逼近,高血量,中等伤害"},
|
||||||
|
|
||||||
5209:{uuid:5209,name:"树人",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"重型坦克型:缓慢逼近,高血量,中等伤害"},
|
buff:[],tal:[],info:"重型坦克型:缓慢逼近,高血量,中等伤害"},
|
||||||
|
|
||||||
// 4. 远程骚扰型
|
// 4. 远程骚扰型
|
||||||
5210:{uuid:5210,name:"骷髅弓手",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"远程骚扰型:保持距离发射箭矢,逼迫玩家走位"},
|
buff:[],tal:[],info:"远程骚扰型:保持距离发射箭矢,逼迫玩家走位"},
|
||||||
|
|
||||||
5211:{uuid:5211,name:"法师骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:"远程骚扰型:保持距离释放法术弹幕,逼迫玩家走位"},
|
buff:[],tal:[],info:"远程骚扰型:保持距离释放法术弹幕,逼迫玩家走位"},
|
||||||
|
|
||||||
// 5. 特殊机制型
|
// 5. 特殊机制型
|
||||||
5212:{uuid:5212,name:"炸弹骷髅",path:"mo1", fac:FacSet.MON, kind:1,as:1.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:"特殊机制:接近玩家后自爆造成高额伤害,需优先击杀"},
|
buff:[],tal:[],info:"特殊机制:接近玩家后自爆造成高额伤害,需优先击杀"},
|
||||||
|
|
||||||
// 6. 精英/BOSS型
|
// 6. 精英/BOSS型
|
||||||
5213:{uuid:5213,name:"亡灵领主(精英)",path:"mo1", fac:FacSet.MON, kind:1,as:1.5,
|
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:高血量与独特机制,波次高潮与重要经验来源"},
|
buff:[],tal:[],info:"精英/BOSS:高血量与独特机制,波次高潮与重要经验来源"},
|
||||||
|
|
||||||
// 5. 特殊机制扩展
|
// 5. 特殊机制扩展
|
||||||
@@ -204,13 +204,13 @@ export const HeroInfo: Record<number, heroInfo> = {
|
|||||||
// 治疗者:为周围怪物回血(此处以提升治疗效果和生命回复为基础被动)
|
// 治疗者:为周围怪物回血(此处以提升治疗效果和生命回复为基础被动)
|
||||||
// Attrs.HEAL_EFFECT=5 (RATIO=1),Attrs.HP_REGEN=3 (VALUE=0)
|
// 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,
|
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:[],tal:[],info:"特殊机制:为周围怪物提供治疗增益与持续回复"},
|
||||||
|
|
||||||
// 光环怪:为周围怪物提供增益(此处以Buff效果提升与移动速度提升为基础被动)
|
// 光环怪:为周围怪物提供增益(此处以Buff效果提升与移动速度提升为基础被动)
|
||||||
// Attrs.BUFF_UP=60 (RATIO=1),Attrs.SPEED=63 (RATIO=1)
|
// 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,
|
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:"特殊机制:为周围怪物提供增益光环,加速与增益效果强化"},
|
buff:[],tal:[],info:"特殊机制:为周围怪物提供增益光环,加速与增益效果强化"},
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -74,7 +74,7 @@ export class Hero extends ecs.Entity {
|
|||||||
model.is_master = true;
|
model.is_master = true;
|
||||||
|
|
||||||
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
||||||
skillsComp.initSkills(hero.skills,uuid);
|
skillsComp.initSkills(hero.skills, uuid, this);
|
||||||
|
|
||||||
// 设置基础属性
|
// 设置基础属性
|
||||||
model.base_ap = hero.ap;
|
model.base_ap = hero.ap;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { smc } from "../common/SingletonModuleComp";
|
|||||||
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
|
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
|
||||||
import { BuffConf, SkillSet } from "../common/config/SkillSet";
|
import { BuffConf, SkillSet } from "../common/config/SkillSet";
|
||||||
import { HeroInfo, AttrSet, HeroUpSet } from "../common/config/heroSet";
|
import { HeroInfo, AttrSet, HeroUpSet } from "../common/config/heroSet";
|
||||||
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
|
|
||||||
|
|
||||||
@ecs.register('HeroAttrs')
|
@ecs.register('HeroAttrs')
|
||||||
@@ -33,6 +34,10 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
Attrs: any = []; // 最终属性数组(经过Buff计算后)
|
Attrs: any = []; // 最终属性数组(经过Buff计算后)
|
||||||
NeAttrs: any = []; // 负面状态数组
|
NeAttrs: any = []; // 负面状态数组
|
||||||
|
|
||||||
|
// ==================== 技能距离缓存 ====================
|
||||||
|
maxSkillDistance: number = 0; // 最远技能攻击距离(缓存,受MP影响)
|
||||||
|
minSkillDistance: number = 0; // 最近技能攻击距离(缓存,不受MP影响,用于停止位置判断)
|
||||||
|
|
||||||
// ==================== Buff/Debuff 系统 ====================
|
// ==================== Buff/Debuff 系统 ====================
|
||||||
/** 持久型buff数组 - 不会自动过期 */
|
/** 持久型buff数组 - 不会自动过期 */
|
||||||
BUFFS: Record<number, Array<{value: number, BType: BType}>> = {};
|
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;
|
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() {
|
reset() {
|
||||||
@@ -370,6 +410,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
this.NeAttrs = [];
|
this.NeAttrs = [];
|
||||||
this.BUFFS = {};
|
this.BUFFS = {};
|
||||||
this.BUFFS_TEMP = {};
|
this.BUFFS_TEMP = {};
|
||||||
|
// 重置技能距离缓存
|
||||||
|
this.maxSkillDistance = 0;
|
||||||
|
this.minSkillDistance = 0;
|
||||||
this.is_dead = false;
|
this.is_dead = false;
|
||||||
this.is_count_dead = false;
|
this.is_count_dead = false;
|
||||||
this.is_atking = false;
|
this.is_atking = false;
|
||||||
@@ -457,6 +500,9 @@ export class HeroAttrSystem extends ecs.ComblockSystem
|
|||||||
|
|
||||||
// 1. 更新临时 Buff/Debuff(时间递减,过期自动移除)
|
// 1. 更新临时 Buff/Debuff(时间递减,过期自动移除)
|
||||||
model.updateTemporaryBuffsDebuffs(this.dt);
|
model.updateTemporaryBuffsDebuffs(this.dt);
|
||||||
|
// 记录MP变化前的值
|
||||||
|
const oldMp = model.mp;
|
||||||
|
|
||||||
if(this.timer.update(this.dt)){
|
if(this.timer.update(this.dt)){
|
||||||
// 2. HP/MP 自然回复(业务规则)
|
// 2. HP/MP 自然回复(业务规则)
|
||||||
model.mp += HeroUpSet.MP
|
model.mp += HeroUpSet.MP
|
||||||
@@ -471,6 +517,14 @@ export class HeroAttrSystem extends ecs.ComblockSystem
|
|||||||
model.hp = model.Attrs[Attrs.HP_MAX];
|
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 帧输出一次统计
|
// 每 60 帧输出一次统计
|
||||||
this.frameCount++;
|
this.frameCount++;
|
||||||
if (this.frameCount % 60 === 0 && this.entityCount === 1) {
|
if (this.frameCount % 60 === 0 && this.entityCount === 1) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { HeroViewComp } from "./HeroViewComp";
|
import { HeroViewComp } from "./HeroViewComp";
|
||||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||||
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { FacSet } from "../common/config/GameSet";
|
import { FacSet } from "../common/config/GameSet";
|
||||||
import { HType } from "../common/config/heroSet";
|
import { HType } from "../common/config/heroSet";
|
||||||
@@ -30,7 +31,7 @@ export class HeroMoveComp extends ecs.Comp {
|
|||||||
@ecs.register('HeroMoveSystem')
|
@ecs.register('HeroMoveSystem')
|
||||||
export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
filter(): ecs.IMatcher {
|
filter(): ecs.IMatcher {
|
||||||
return ecs.allOf(HeroMoveComp, HeroViewComp, HeroAttrsComp);
|
return ecs.allOf(HeroMoveComp, HeroViewComp, HeroAttrsComp, HeroSkillsComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(e: ecs.Entity) {
|
update(e: ecs.Entity) {
|
||||||
@@ -44,8 +45,10 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
if (model.fac !== FacSet.HERO) return;
|
if (model.fac !== FacSet.HERO) return;
|
||||||
if (!move.moving) return;
|
if (!move.moving) return;
|
||||||
|
|
||||||
const shouldStop = this.checkEnemiesInFace(e);
|
const shouldStopInFace = this.checkEnemiesInFace(e);
|
||||||
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
|
const shouldStopAtMinRange = this.shouldStopAtMinSkillRange(e);
|
||||||
|
const shouldStop = shouldStopInFace || shouldStopAtMinRange;
|
||||||
|
model.is_atking = this.checkEnemiesInSkillRange(e);
|
||||||
|
|
||||||
// 更新渲染层级
|
// 更新渲染层级
|
||||||
this.updateRenderOrder(e);
|
this.updateRenderOrder(e);
|
||||||
@@ -236,6 +239,36 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
return found;
|
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) {
|
private updateRenderOrder(entity: ecs.Entity) {
|
||||||
const currentView = entity.get(HeroViewComp);
|
const currentView = entity.get(HeroViewComp);
|
||||||
@@ -283,4 +316,31 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ export interface SkillSlot {
|
|||||||
cd_max: number; // 最大CD时间
|
cd_max: number; // 最大CD时间
|
||||||
cost: number; // MP消耗
|
cost: number; // MP消耗
|
||||||
level: number; // 技能等级(预留)
|
level: number; // 技能等级(预留)
|
||||||
|
dis: number; // 攻击距离
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,8 +42,10 @@ export class HeroSkillsComp extends ecs.Comp {
|
|||||||
/**
|
/**
|
||||||
* 初始化技能列表
|
* 初始化技能列表
|
||||||
* @param sUuids 技能配置ID数组
|
* @param sUuids 技能配置ID数组
|
||||||
|
* @param uuid 英雄UUID
|
||||||
|
* @param entity 实体对象(用于更新技能距离缓存)
|
||||||
*/
|
*/
|
||||||
initSkills(sUuids: number[], uuid: number) {
|
initSkills(sUuids: number[], uuid: number, entity?: ecs.Entity) {
|
||||||
this.skills = [];
|
this.skills = [];
|
||||||
for (let i = 0; i < sUuids.length; i++) {
|
for (let i = 0; i < sUuids.length; i++) {
|
||||||
const s_uuid = sUuids[i];
|
const s_uuid = sUuids[i];
|
||||||
@@ -59,14 +62,25 @@ export class HeroSkillsComp extends ecs.Comp {
|
|||||||
cd_max: cdMax,
|
cd_max: cdMax,
|
||||||
cost: config.cost,
|
cost: config.cost,
|
||||||
level: 1,
|
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];
|
const config = SkillSet[s_uuid];
|
||||||
if (!config) {
|
if (!config) {
|
||||||
console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`);
|
console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`);
|
||||||
@@ -77,8 +91,17 @@ export class HeroSkillsComp extends ecs.Comp {
|
|||||||
cd: 0,
|
cd: 0,
|
||||||
cd_max: config.cd,
|
cd_max: config.cd,
|
||||||
cost: config.cost,
|
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;
|
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() {
|
reset() {
|
||||||
this.skills = {};
|
this.skills = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export class Monster extends ecs.Entity {
|
|||||||
model.Attrs[Attrs.DIS] = hero.dis;
|
model.Attrs[Attrs.DIS] = hero.dis;
|
||||||
|
|
||||||
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
||||||
skillsComp.initSkills(hero.skills,uuid);
|
skillsComp.initSkills(hero.skills, uuid, this);
|
||||||
|
|
||||||
this.add(view);
|
this.add(view);
|
||||||
oops.message.dispatchEvent("monster_load",this)
|
oops.message.dispatchEvent("monster_load",this)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { HeroViewComp } from "./HeroViewComp";
|
import { HeroViewComp } from "./HeroViewComp";
|
||||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||||
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { FacSet, IndexSet } from "../common/config/GameSet";
|
import { FacSet, IndexSet } from "../common/config/GameSet";
|
||||||
import { Attrs } from "../common/config/HeroAttrs";
|
import { Attrs } from "../common/config/HeroAttrs";
|
||||||
@@ -32,7 +33,7 @@ export class MonMoveComp extends ecs.Comp {
|
|||||||
@ecs.register('MonMoveSystem')
|
@ecs.register('MonMoveSystem')
|
||||||
export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
filter(): ecs.IMatcher {
|
filter(): ecs.IMatcher {
|
||||||
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp);
|
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp, HeroSkillsComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(e: ecs.Entity) {
|
update(e: ecs.Entity) {
|
||||||
@@ -46,8 +47,10 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
|||||||
if (model.fac !== FacSet.MON) return;
|
if (model.fac !== FacSet.MON) return;
|
||||||
if (!move.moving) return;
|
if (!move.moving) return;
|
||||||
|
|
||||||
const shouldStop = this.checkEnemiesInFace(e);
|
const shouldStopInFace = this.checkEnemiesInFace(e);
|
||||||
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
|
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;
|
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 {
|
private checkEnemiesInFace(entity: ecs.Entity): boolean {
|
||||||
const currentView = entity.get(HeroViewComp);
|
const currentView = entity.get(HeroViewComp);
|
||||||
@@ -132,4 +165,31 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
|||||||
});
|
});
|
||||||
return found;
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,9 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
|
|||||||
const config = SkillSet[skill.s_uuid];
|
const config = SkillSet[skill.s_uuid];
|
||||||
if (!config || config.SType !== SType.damage) continue;
|
if (!config || config.SType !== SType.damage) continue;
|
||||||
|
|
||||||
|
// 检查是否有敌人在技能攻击范围内
|
||||||
|
if (!this.hasEnemyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
|
||||||
|
|
||||||
// ✅ 开始执行施法
|
// ✅ 开始执行施法
|
||||||
this.startCast(e,skill);
|
this.startCast(e,skill);
|
||||||
|
|
||||||
@@ -277,5 +280,30 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
|
|||||||
return allies;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user