Compare commits
2 Commits
914ab0e8b9
...
4670b12330
| Author | SHA1 | Date | |
|---|---|---|---|
| 4670b12330 | |||
| 8152523e10 |
@@ -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%攻击的伤害,有一定几率施加灼烧",
|
||||
},
|
||||
|
||||
|
||||
@@ -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:"特殊机制:为周围怪物提供增益光环,加速与增益效果强化"},
|
||||
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 = {};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ export class HeroViewComp extends CCComp {
|
||||
useMp:boolean = false;
|
||||
realDeadTime:number=10
|
||||
deadCD:number=0
|
||||
// 血条显示相关
|
||||
hpBarShowTime:number = 5; // 血条显示持续时间(秒)
|
||||
hpBarShowCD:number = 0; // 血条显示计时器
|
||||
// ==================== UI 节点引用 ====================
|
||||
private top_node: Node = null!;
|
||||
|
||||
@@ -89,6 +92,8 @@ export class HeroViewComp extends CCComp {
|
||||
this.useMp=HeroInfo[this.model.hero_uuid].type==HType.mage||HeroInfo[this.model.hero_uuid].type==HType.remote||HeroInfo[this.model.hero_uuid].type==HType.support;
|
||||
this.top_node.getChildByName("pow").active = this.usePower;
|
||||
this.top_node.getChildByName("mp").active = this.useMp;
|
||||
// 初始隐藏血条(被攻击后才显示)
|
||||
this.top_node.active = false;
|
||||
}
|
||||
|
||||
/** 初始化 UI 节点引用 */
|
||||
@@ -119,6 +124,17 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
return
|
||||
} ;
|
||||
|
||||
// 处理血条显示计时
|
||||
if (this.hpBarShowCD > 0) {
|
||||
this.hpBarShowCD -= dt;
|
||||
if (this.hpBarShowCD <= 0) {
|
||||
// 时间到,隐藏血条
|
||||
this.top_node.active = false;
|
||||
this.hpBarShowCD = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ View 层职责:处理表现相关的逻辑
|
||||
this.processDamageQueue(); // 伤害数字显示队列
|
||||
|
||||
@@ -144,11 +160,9 @@ export class HeroViewComp extends CCComp {
|
||||
|
||||
/** 显示血量 */
|
||||
private hp_show(hp: number, hp_max: number) {
|
||||
if(hp==hp_max) {
|
||||
this.top_node.active=false;
|
||||
return
|
||||
}
|
||||
this.top_node.active=true
|
||||
// 不再基于血量是否满来决定显示状态,只更新进度条
|
||||
if(!this.top_node.active) return;
|
||||
|
||||
let hp_progress = hp / hp_max;
|
||||
this.top_node.getChildByName("hp").getComponent(ProgressBar).progress = hp_progress;
|
||||
this.scheduleOnce(() => {
|
||||
@@ -292,6 +306,7 @@ export class HeroViewComp extends CCComp {
|
||||
this.status_change("idle");
|
||||
this.model.hp =this.model.Attrs[Attrs.HP_MAX]*50/100;
|
||||
this.top_node.active=false
|
||||
this.hpBarShowCD=0
|
||||
|
||||
}
|
||||
/**
|
||||
@@ -334,7 +349,12 @@ export class HeroViewComp extends CCComp {
|
||||
|
||||
}
|
||||
do_atked(damage:number,isCrit:boolean,s_uuid:number){
|
||||
// 受到攻击时显示血条,并设置显示时间(即使伤害为0也显示)
|
||||
this.top_node.active = true;
|
||||
this.hpBarShowCD = this.hpBarShowTime;
|
||||
|
||||
if (damage <= 0) return;
|
||||
|
||||
// 视图层表现
|
||||
let SConf=SkillSet[s_uuid]
|
||||
this.back()
|
||||
@@ -448,6 +468,7 @@ export class HeroViewComp extends CCComp {
|
||||
collider.off(Contact2DType.BEGIN_CONTACT);
|
||||
}
|
||||
this.deadCD=0
|
||||
this.hpBarShowCD=0
|
||||
|
||||
// 清理伤害队列
|
||||
this.damageQueue.length = 0;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -277,5 +280,30 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user