refactor(英雄属性): 重构属性系统为扁平结构并移除技能组件
- 将 Attrs 枚举从数字索引改为字符串键值对,直接映射到 HeroAttrsComp 的字段 - 删除 HeroSkillsComp 组件,将攻击和技能计时器移至 HeroAttrsComp - 移除复杂的属性类型映射和初始化函数,简化属性访问逻辑 - 更新 HeroViewComp 以直接使用模型字段而非通过 Attrs 枚举 - 重命名 NeAttrs 为 Debuff 并调整相关配置接口
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
|
||||
import { BType } from "../common/config/HeroAttrs";
|
||||
import { BuffConf, SkillRange } from "../common/config/SkillSet";
|
||||
import { HeroInfo, AttrSet, HType, JobUpConf } from "../common/config/heroSet";
|
||||
import { HeroSkillsComp } from "./HeroSkills";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator } from "cc";
|
||||
|
||||
@@ -37,9 +35,10 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
shield_max: number = 0; // 最大护盾值
|
||||
|
||||
// ==================== 攻击属性 (补充) ====================
|
||||
as: number = 0; // 攻击速度(减少技能skills[0]CD)
|
||||
ss: number = 0; // 技能速度 (减少skills[0] 以外的cd)
|
||||
|
||||
a_cd: number = 0; // 攻击计时
|
||||
s_cd: number = 0; // 技能计时
|
||||
a_cd_max: number = 0; // 攻击CD
|
||||
s_cd_max: number = 0; // 技能CD
|
||||
// ==================== 暴击与命中属性 ====================
|
||||
critical: number = 0; // 暴击率
|
||||
critical_dmg: number = 0; // 暴击伤害
|
||||
@@ -59,6 +58,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
puncture: number = 0; // 穿刺次数
|
||||
puncture_dmg: number = 0; // 穿刺伤害
|
||||
wfuny: number = 0; // 风怒
|
||||
|
||||
|
||||
|
||||
boom: boolean = false; // 自爆怪
|
||||
|
||||
|
||||
@@ -91,10 +93,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
atk_count: number = 0; // 攻击次数
|
||||
atked_count: number = 0; // 被攻击次数
|
||||
killed_count:number=0;
|
||||
// 注意:技能数据已迁移到 HeroSkillsComp,不再存储在这里
|
||||
atk_id:number=0; //普通攻击技能id
|
||||
skill_id:number=0; //技能攻击技能id
|
||||
|
||||
|
||||
|
||||
start(){
|
||||
}
|
||||
// ==================== BUFF 系统初始化 ====================
|
||||
@@ -216,8 +217,10 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.shield_max = 0;
|
||||
|
||||
// 重置新增属性
|
||||
this.as = 0;
|
||||
this.ss = 0;
|
||||
this.a_cd = 0;
|
||||
this.s_cd = 0;
|
||||
this.a_cd_max = 0;
|
||||
this.s_cd_max = 0;
|
||||
this.critical = 0;
|
||||
this.critical_dmg = 0;
|
||||
this.freeze_chance = 0;
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { HSSet, SkillSet } from "../common/config/SkillSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator } from "cc";
|
||||
|
||||
const { property } = _decorator;
|
||||
|
||||
/**
|
||||
* ==================== 技能槽位数据 ====================
|
||||
* 单个技能的运行时数据
|
||||
*/
|
||||
export interface SkillSlot {
|
||||
s_uuid: number; // 技能配置ID
|
||||
cd: number; // 当前CD时间(递减)
|
||||
cd_max: number; // 最大CD时间
|
||||
level: number; // 技能等级(预留)
|
||||
dis: number; // 攻击距离
|
||||
hset: HSSet; // 技能设定, 0:普通攻击, 1:一般技能, 2:必杀技
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ==================== 英雄技能数据组件 ====================
|
||||
*
|
||||
* 职责:
|
||||
* 1. 存储角色拥有的技能列表
|
||||
* 2. 管理技能CD状态
|
||||
* 3. 提供技能查询接口
|
||||
*
|
||||
* 设计理念:
|
||||
* - 只存数据,不含施法逻辑
|
||||
* - CD 更新由 HSkillSystem 负责
|
||||
* - 施法判定由 HSkillSystem 负责
|
||||
*/
|
||||
@ecs.register('HeroSkills')
|
||||
export class HeroSkillsComp extends ecs.Comp {
|
||||
@property({ tooltip: "是否启用调试日志" })
|
||||
private debugMode: boolean = false;
|
||||
|
||||
// ==================== 技能槽位列表 ====================
|
||||
/** 技能槽位数组(最多4个技能) */
|
||||
skills: Record<number, SkillSlot> = {};
|
||||
max_auto: boolean = true;
|
||||
|
||||
/** AI 检测计时器 */
|
||||
ai_timer: number = 0;
|
||||
|
||||
onLoad() {
|
||||
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
/**
|
||||
* 初始化技能列表
|
||||
* @param sUuids 技能配置ID数组
|
||||
* @param uuid 英雄UUID
|
||||
* @param entity 实体对象(用于更新技能距离缓存)
|
||||
*/
|
||||
initSkills(sUuids: number[], uuid: number) {
|
||||
this.skills = [];
|
||||
for (let i = 0; i < sUuids.length; i++) {
|
||||
const s_uuid = sUuids[i];
|
||||
const config = SkillSet[s_uuid];
|
||||
if (!config) {
|
||||
mLogger.warn(this.debugMode, 'HeroSkills', `[HeroSkills] 技能配置不存在: ${s_uuid}`);
|
||||
continue;
|
||||
}
|
||||
// 第0个技能的 cd_max 取 herosinfo[uuid].as
|
||||
const cdMax = i === 0 ? HeroInfo[uuid].as : config.cd;
|
||||
let hset = HSSet.atk;
|
||||
if(i ===1) hset = HSSet.skill;
|
||||
if(i ===2) hset = HSSet.max;
|
||||
this.skills[s_uuid] = {
|
||||
s_uuid: config.uuid,
|
||||
cd: 0,
|
||||
cd_max: cdMax,
|
||||
level: 1,
|
||||
dis: Number(config.dis),
|
||||
hset: hset,
|
||||
};
|
||||
}
|
||||
|
||||
// 更新技能距离缓存
|
||||
if (this.ent) {
|
||||
const attrsComp = this.ent.get(HeroAttrsComp);
|
||||
if (attrsComp) {
|
||||
attrsComp.updateSkillDistanceCache(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单个技能
|
||||
* @param s_uuid 技能配置ID
|
||||
* @param hset 技能类型
|
||||
*/
|
||||
addSkill(s_uuid: number, hset: HSSet=HSSet.skill) {
|
||||
const config = SkillSet[s_uuid];
|
||||
if (!config) {
|
||||
mLogger.warn(this.debugMode, 'HeroSkills', `[HeroSkills] 技能配置不存在: ${s_uuid}`);
|
||||
return;
|
||||
}
|
||||
this.skills[s_uuid] = {
|
||||
s_uuid: config.uuid,
|
||||
cd: 0,
|
||||
cd_max: config.cd,
|
||||
level: 1,
|
||||
dis: Number(config.dis),
|
||||
hset: hset,
|
||||
};
|
||||
|
||||
// 更新技能距离缓存
|
||||
if (this.ent) {
|
||||
const attrsComp = this.ent.get(HeroAttrsComp);
|
||||
if (attrsComp) {
|
||||
attrsComp.updateSkillDistanceCache(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定s_uuid的技能
|
||||
*/
|
||||
getSkill(s_uuid: number): SkillSlot | null {
|
||||
return this.skills[s_uuid] ?? null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查技能是否可施放(通过s_uuid)
|
||||
* @param s_uuid 技能配置ID
|
||||
*/
|
||||
canCast(s_uuid: number): boolean {
|
||||
const skill = this.getSkill(s_uuid);
|
||||
if (!skill) return false;
|
||||
|
||||
// 检查CD
|
||||
return skill.cd <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置技能CD(开始冷却,通过索引)
|
||||
*/
|
||||
resetCD(s_uuid: number) {
|
||||
let attrsCom = this.ent.get(HeroAttrsComp);
|
||||
if (!attrsCom) return;
|
||||
const skill = this.getSkill(s_uuid);
|
||||
if (!skill) return;
|
||||
|
||||
const speedAttr = skill.hset === HSSet.atk ? Attrs.AS : Attrs.SS;
|
||||
const rawSpeed = attrsCom.Attrs?.[speedAttr] ?? 0;
|
||||
const speedBonus = Math.max(-0.9, rawSpeed / 100);
|
||||
const speedMultiplier = 1 / (1 + speedBonus);
|
||||
skill.cd = Math.max(0, skill.cd_max * speedMultiplier);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新所有技能CD(每帧调用)
|
||||
* @param dt 时间增量
|
||||
*/
|
||||
updateCDs(dt: number) {
|
||||
for (const s_uuid in this.skills) {
|
||||
const skill = this.skills[Number(s_uuid)];
|
||||
if (skill.cd > 0) {
|
||||
skill.cd -= dt;
|
||||
if (skill.cd < 0) {
|
||||
skill.cd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可施放的技能索引
|
||||
*/
|
||||
getReadySkills(): number[] {
|
||||
const ready: number[] = [];
|
||||
for (const s_uuid in this.skills) {
|
||||
if (this.canCast(Number(s_uuid))) {
|
||||
ready.push(Number(s_uuid));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可施放技能中的最远攻击距离
|
||||
* @returns 最远攻击距离,如果没有可用技能返回0
|
||||
*/
|
||||
getMaxSkillDistance(): number {
|
||||
const readySkills = this.getReadySkills();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可施放技能中的最近攻击距离
|
||||
* @returns 最近攻击距离,如果没有可用技能返回0
|
||||
*/
|
||||
getMinSkillDistance(): number {
|
||||
const readySkills = this.getReadySkills();
|
||||
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 = {};
|
||||
}
|
||||
setMaxAuto(on: boolean) {
|
||||
this.max_auto = on;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,7 @@ import { smc } from "../common/SingletonModuleComp";
|
||||
import { EAnmConf, SkillSet,} from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { TooltipTypes } from "../common/config/GameSet";
|
||||
import { Attrs, } from "../common/config/HeroAttrs";
|
||||
import { TooltipTypes } from "../common/config/GameSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { Tooltip } from "../skill/Tooltip";
|
||||
import { timedCom } from "../skill/timedCom";
|
||||
@@ -113,7 +112,6 @@ export class HeroViewComp extends CCComp {
|
||||
this.top_node = this.node.getChildByName("top");
|
||||
// let hp_y = this.node.getComponent(UITransform).height+10;
|
||||
// this.top_node.setPosition(0, hp_y, 0);
|
||||
smc.updateHeroInfo(this.model)
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +160,7 @@ export class HeroViewComp extends CCComp {
|
||||
// }
|
||||
|
||||
if (this.model.dirty_shield) {
|
||||
this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]);
|
||||
this.show_shield(this.model.shield, this.model.shield_max);
|
||||
this.model.dirty_shield = false;
|
||||
}
|
||||
}
|
||||
@@ -186,7 +184,7 @@ export class HeroViewComp extends CCComp {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
// 不再基于血量是否满来决定显示状态,只更新进度条
|
||||
let hp=this.model.hp;
|
||||
let hp_max=this.model.Attrs[Attrs.HP_MAX];
|
||||
let hp_max=this.model.hp_max;
|
||||
// mLogger.log(this.debugMode, 'HeroViewComp', "hp_show",hp,hp_max)
|
||||
|
||||
let targetProgress = hp / hp_max;
|
||||
@@ -209,18 +207,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
/** 显示魔法值 */
|
||||
private mp_show() {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
if(!this.top_node.active) return
|
||||
let mp=this.model.mp;
|
||||
let mp_max=this.model.Attrs[Attrs.MP_MAX];
|
||||
mLogger.log(this.debugMode, 'HeroViewComp', "mp_show",mp,mp_max)
|
||||
this.top_node.getChildByName("mp").getComponent(ProgressBar).progress = mp / mp_max;
|
||||
this.scheduleOnce(() => {
|
||||
this.top_node.getChildByName("mp").getChildByName("mpb").getComponent(ProgressBar).progress = mp / mp_max;
|
||||
}, 0.15);
|
||||
}
|
||||
|
||||
|
||||
/** 升级特效 */
|
||||
private lv_up() {
|
||||
@@ -342,7 +329,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
add_shield(shield:number){
|
||||
// 护盾数据更新由 Model 层处理,这里只负责视图表现
|
||||
if(this.model && this.model.shield>0) this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]);
|
||||
if(this.model && this.model.shield>0) this.show_shield(this.model.shield, this.model.shield_max);
|
||||
}
|
||||
|
||||
health(hp: number = 0) {
|
||||
@@ -369,7 +356,7 @@ export class HeroViewComp extends CCComp {
|
||||
this.model.is_count_dead=false
|
||||
this.as.do_buff();
|
||||
this.status_change("idle");
|
||||
this.model.hp =this.model.Attrs[Attrs.HP_MAX]*50/100;
|
||||
this.model.hp =this.model.hp_max*50/100;
|
||||
this.top_node.active=false
|
||||
this.lastBarUpdateTime=0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user