Files
heros/assets/script/game/hero/HeroSkills.ts
walkpan 914ab0e8b9 feat(技能系统): 实现技能冷却时间受攻击速度和技能速度属性影响
新增技能速度(SS)属性,用于减少非基础攻击技能的冷却时间
基础攻击技能冷却时间由英雄的as属性决定
眩晕和冰冻状态下不更新技能CD
合并冗余的canCast和resetCD方法
2025-11-03 20:53:31 +08:00

162 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Attrs } from "../common/config/HeroAttrs";
import { HeroInfo } from "../common/config/heroSet";
import { SkillSet } from "../common/config/SkillSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
/**
* ==================== 技能槽位数据 ====================
* 单个技能的运行时数据
*/
export interface SkillSlot {
s_uuid: number; // 技能配置ID
cd: number; // 当前CD时间递减
cd_max: number; // 最大CD时间
cost: number; // MP消耗
level: number; // 技能等级(预留)
}
/**
* ==================== 英雄技能数据组件 ====================
*
* 职责:
* 1. 存储角色拥有的技能列表
* 2. 管理技能CD状态
* 3. 提供技能查询接口
*
* 设计理念:
* - 只存数据,不含施法逻辑
* - CD 更新由 HSkillSystem 负责
* - 施法判定由 HSkillSystem 负责
*/
@ecs.register('HeroSkills')
export class HeroSkillsComp extends ecs.Comp {
// ==================== 技能槽位列表 ====================
/** 技能槽位数组最多4个技能 */
skills: Record<number, SkillSlot> = {};
// ==================== 辅助方法 ====================
/**
* 初始化技能列表
* @param sUuids 技能配置ID数组
*/
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) {
console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`);
continue;
}
// 第0个技能的 cd_max 取 herosinfo[uuid].as
const cdMax = i === 0 ? HeroInfo[uuid].as : config.cd;
this.skills[s_uuid] = {
s_uuid: config.uuid,
cd: 0,
cd_max: cdMax,
cost: config.cost,
level: 1,
};
}
}
/**
* 添加单个技能
*/
addSkill(s_uuid: number) {
const config = SkillSet[s_uuid];
if (!config) {
console.warn(`[HeroSkills] 技能配置不存在: ${s_uuid}`);
return;
}
this.skills[s_uuid] = {
s_uuid: config.uuid,
cd: 0,
cd_max: config.cd,
cost: config.cost,
level: 1
};
}
/**
* 获取指定s_uuid的技能
*/
getSkill(s_uuid: number): SkillSlot | null {
return this.skills[s_uuid] ?? null;
}
/**
* 检查技能是否可施放通过s_uuid
* @param s_uuid 技能配置ID
* @param currentMp 当前MP值
*/
canCast(s_uuid: number, currentMp: number): boolean {
const skill = this.getSkill(s_uuid);
if (!skill) return false;
// 检查CD和MP
return skill.cd <= 0 && currentMp >= skill.cost;
}
/**
* 重置技能CD开始冷却通过索引
*/
resetCD(s_uuid: number) {
let attrsCom = this.ent.get(HeroAttrsComp);
if (!attrsCom) return;
const skill = this.getSkill(s_uuid);
if (!skill) return;
// 普通攻击skills[0])受 AS 影响,其余技能受 SS 影响
const isNormalAttack = s_uuid === this.skills[0]?.s_uuid;
const speedAttr = isNormalAttack ? Attrs.AS : Attrs.SS;
const speedBonus = attrsCom.Attrs[speedAttr] / 100; // 100 表示 100% 提速
const speedMultiplier = 1 / (1 + speedBonus); // 提速 100% => cd 减半
skill.cd = skill.cd_max * speedMultiplier;
if (skill) {
skill.cd = skill.cd_max;
}
}
/**
* 更新所有技能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(currentMp: number): number[] {
const ready: number[] = [];
for (const s_uuid in this.skills) {
if (this.canCast(Number(s_uuid), currentMp)) {
ready.push(Number(s_uuid));
}
}
return ready;
}
reset() {
this.skills = {};
}
}