feat: 新增闪避与命中属性系统,完善战斗判定逻辑

1.  新增全局默认闪避/命中率配置,为英雄和怪物提供缺省属性兜底
2.  扩展英雄属性组件,添加闪避、命中率属性与对应计算接口
3.  实现闪避判定逻辑,修正伤害流程,添加闪避提示效果
4.  新增闪避/命中相关buff、技能与光环配置
5.  完善属性值计算与 tooltip 显示逻辑
This commit is contained in:
panFD
2026-09-07 20:41:49 +08:00
parent 22fe31a769
commit aa488d55e7
11 changed files with 123 additions and 1 deletions

View File

@@ -287,4 +287,20 @@ export const BuffList: Record<number, BuffConfig> = {
modifiers: [{ attr: Attrs.hp_max, op: ModOp.PercentAdd, value: 30 }],
info: "最大生命值提升 30%,并立即回复等量生命,持续 5 秒",
},
// ==================== 闪避 / 命中系 ====================
// 闪避爆发(限时提升闪避概率,百分点累加)
7030: {
id: 7030, name: "闪避爆发", icon: "98-Enchant-Wind",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.dodge, op: ModOp.PercentAdd, value: 20 }],
info: "闪避概率提升 20%,持续 5 秒",
},
// 命中强化(限时提升命中概率,百分点累加)
7031: {
id: 7031, name: "命中强化", icon: "27-Hawkeye",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.accuracy, op: ModOp.PercentAdd, value: 20 }],
info: "命中概率提升 20%,持续 5 秒",
},
};

View File

@@ -76,6 +76,10 @@ export enum FightSet {
INIT_REFRESH_STONE = 3, // 初始刷新石数量
/** 单次刷新消耗的刷新石数量 */
REFRESH_STONE_COST = 1,
/** 全局默认闪避率(百分点),heroSet 未配置时生效 */
DEFAULT_DODGE = 15,
/** 全局默认命中率(百分点),heroSet 未配置时生效 */
DEFAULT_ACCURACY = 10,
}
/**
@@ -143,6 +147,7 @@ export const TooltipTypes = {
hpup: 8,
addmp: 9,
shield: 10,
miss: 11,
}

View File

@@ -455,6 +455,16 @@ export const SkillSet: Record<number, SkillConfig> = {
DTType: DTType.single, kind: SkillKind.Support, ap: 10, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.thorns_rate, info: "全体友方比例反伤提升10%, 永久生效",
},
6415: {
uuid: 6415, name: "闪避强化", sp_name: "buff_wind", icon: "98-Enchant-Wind", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 5, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.dodge, info: "全体友方闪避概率提升5%, 永久生效",
},
6416: {
uuid: 6416, name: "命中强化", sp_name: "buff_wind", icon: "27-Hawkeye", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 5, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.accuracy, info: "全体友方命中概率提升5%, 永久生效",
},
6501: {
uuid: 6501, name: "复活", sp_name: "buff_wind", icon: "40-Holy", TGroup: TGroup.Self, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 50, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
@@ -546,6 +556,16 @@ export const SkillSet: Record<number, SkillConfig> = {
uuid: 6517, name: "生命爆发", sp_name: "buff_wind", icon: "29-Endure", TGroup: TGroup.Team, readyAnm: "up_green", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.hp_max, timed_buff_id: 7029, info: "全体友方最大生命值提升30%, 持续5秒",
},
6518: {
uuid: 6518, name: "闪避爆发", sp_name: "buff_wind", icon: "98-Enchant-Wind", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.dodge, timed_buff_id: 7030, info: "全体友方闪避概率提升20%, 持续5秒",
},
6519: {
uuid: 6519, name: "命中强化", sp_name: "buff_wind", icon: "27-Hawkeye", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.accuracy, timed_buff_id: 7031, info: "全体友方命中概率提升20%, 持续5秒",
}
};
//***************驻场技能配置***************
@@ -569,6 +589,8 @@ export enum FieldSkillType {
HeroPunctureDmg = 21, // 英雄穿透后伤害加成(百分点;乘算:PUNCTURE_DMG_RATIO * (1 + bonus/100))
HeroThornsDmg = 22, // 英雄固定反伤加成
HeroThornsRate = 23, // 英雄比例反伤加成(百分点)
HeroDodge = 24, // 英雄闪避率加成(百分点)
HeroHit = 25, // 英雄命中率加成(百分点)
}
export interface FieldSkillConfig {
@@ -607,4 +629,6 @@ export const FieldSkillSet: Record<number, FieldSkillConfig> = {
7016: { uuid: 7016, name: "鼓舞光环", icon: "64-Prominance", type: FieldSkillType.StartCount, info: "战斗开始触发技能次数提升" },
7018: { uuid: 7018, name: "连击光环", icon: "74-Satisfaction", type: FieldSkillType.AtkCount, info: "攻击触发技能次数提升" },
7019: { uuid: 7019, name: "受击光环", icon: "84-Termor", type: FieldSkillType.BeAtkCount, info: "被攻击触发技能次数提升" },
7023: { uuid: 7023, name: "闪避光环", icon: "98-Enchant-Wind", type: FieldSkillType.HeroDodge, info: "英雄闪避概率提升" },
7024: { uuid: 7024, name: "命中光环", icon: "27-Hawkeye", type: FieldSkillType.HeroHit, info: "英雄命中概率提升" },
};

View File

@@ -1,4 +1,4 @@
import { v3 } from "cc"
import { v3 } from "cc"
import { BoxSet, FacSet, FightSet } from "./GameSet"
import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams } from "./SkillSet"
import { BuffList, BuffConfig, ModOp } from "./BuffSet"
@@ -414,6 +414,8 @@ export interface heroInfo {
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数
dis?: number; // 攻击距离(像素)
speed?: number; // 移动速度(像素/秒)
dodge?: number; // 闪避率(百分点,缺省由初始化处兜底为全局默认值)
accuracy?: number; // 命中率(百分点,缺省由初始化处兜底为全局默认值)
skills: Record<number, HSkillInfo>; // 携带技能ID列表
ap_bonus?: LvBonusEntry[]; // 额外攻击加成(按 lv 升序,运行时累加 ≤英雄等级 的所有档)
hp_bonus?: LvBonusEntry[]; // 额外生命加成(按 lv 升序,运行时累加 ≤英雄等级 的所有档)
@@ -1257,6 +1259,10 @@ function calcBuffPermanentEquivalent(buff: BuffConfig, apEff: number, cd: number
case Attrs.speed:
value += (modValue / 100) * dps * weights.W_DPS_PER_SEC * seconds;
break;
case Attrs.dodge:
case Attrs.accuracy:
value += modValue * weights.W_control;
break;
}
}
}
@@ -1298,6 +1304,8 @@ function calcSupportEffectValue(config: SkillConfig, hpEff: number, apEff: numbe
return (value / 100) * (apEff / cd) * 0.05 * weights.W_DPS_PER_SEC * seconds;
case Attrs.stun_chance:
case Attrs.freeze_chance:
case Attrs.dodge:
case Attrs.accuracy:
return value * weights.W_control;
case Attrs.knockback_chance:
// 击退强化已改为击退距离增量,按距离价值折算

View File

@@ -162,6 +162,9 @@ export class Hero extends ecs.Entity {
}
model.speed = hero.speed ?? 200;
// 闪避/命中:配置优先,缺省取全局默认值(百分点)
model.dodge = hero.dodge ?? FightSet.DEFAULT_DODGE;
model.accuracy = hero.accuracy ?? FightSet.DEFAULT_ACCURACY;
// 构建技能表并注入运行时冷却字段 ccd;overrides 按英雄等级 resolve 分档(下游无感知)
model.skills = {};

View File

@@ -134,6 +134,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
// 触发被攻击事件
this.onAttacked(target);
// 闪避判定(DoT 不参与):闪避率 = 目标闪避 − 施法者命中
if (!isDot && this.checkDodge(damageEvent, TAttrsComp)) {
smc.vmdata.scores.dod_count++;
targetView?.showMiss?.();
// 预留:闪避后触发技能(SkillTriggerType.Dodged)在此接入
return reDate;
}
// 暴击判定
// 使用施法者的暴击率属性(damageEvent.Attrs 快照)- 被攻击者的暴击抗性 + 目标出血层数附加的被暴击率
const criticalChance = (damageEvent.Attrs[Attrs.critical] || 0) - TAttrsComp.getFinalCriticalRes() + this.getBleedCritBonus(target);
@@ -528,6 +536,19 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
return (layers?.length ?? 0) * BLEED_CRIT_PER_STACK;
}
/**
* 闪避判定:闪避率 = 目标闪避 − 施法者命中,再按该值判定是否闪避。
* 命中未配置时(accuracy=0)闪避率 = 目标闪避,即无命中加成时闪避全额生效。
* @param damageEvent 伤害事件(含施法者属性快照)
* @param TAttrsComp 目标属性组件
* @returns true=闪避成功,false=命中
*/
private checkDodge(damageEvent: DamageEvent, TAttrsComp: HeroAttrsComp): boolean {
const dodgeChance = TAttrsComp.getFinalDodge() - (damageEvent.Attrs[Attrs.accuracy] || 0);
if (dodgeChance <= 0) return false;
return this.checkChance(dodgeChance);
}
/**
* 计算易伤附加伤害:汇总目标每层易伤 buff 的数值(value_override 优先),再被易伤抗性百分比减免。
* @returns 附加伤害值(向下取整,非负)

View File

@@ -75,6 +75,8 @@ export class HeroAttrsComp extends ecs.Comp {
// ==================== 特殊属性 ====================
critical: number = 0; // 暴击率
critical_res: number = 0; // 暴击抗性
dodge: number = 0; // 闪避率(被攻击时闪避概率,百分点)
accuracy: number = 0; // 命中率(攻击时命中概率,百分点;削减目标闪避)
freeze_chance: number = 0; // 冰冻概率
freeze_res: number = 0; // 冰冻抗性
stun_chance: number = 0; // 击晕概率
@@ -350,6 +352,18 @@ export class HeroAttrsComp extends ecs.Comp {
return this.critical + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroCrit);
}
/** 英雄实时闪避率 = 基础闪避率 + 驻场闪避率。 */
public getRuntimeDodge(): number {
if (this.fac !== FacSet.HERO) return this.dodge;
return this.dodge + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroDodge);
}
/** 英雄实时命中率 = 基础命中率 + 驻场命中率。 */
public getRuntimeAccuracy(): number {
if (this.fac !== FacSet.HERO) return this.accuracy;
return this.accuracy + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroHit);
}
/** 英雄实时冰冻率 = 基础冰冻率 + 驻场冰冻率。 */
public getRuntimeFreezeChance(): number {
if (this.fac !== FacSet.HERO) return this.freeze_chance;
@@ -541,6 +555,20 @@ export class HeroAttrsComp extends ecs.Comp {
return this.critical_res + mods.flatSum + mods.pctSum;
}
/** 最终闪避率 = 驻场实时闪避率 + 限时修饰(加法模型) */
public getFinalDodge(): number {
const base = this.getRuntimeDodge();
const mods = this.aggregateTimedMods(Attrs.dodge);
return base + mods.flatSum + mods.pctSum;
}
/** 最终命中率 = 驻场实时命中率 + 限时修饰(加法模型) */
public getFinalAccuracy(): number {
const base = this.getRuntimeAccuracy();
const mods = this.aggregateTimedMods(Attrs.accuracy);
return base + mods.flatSum + mods.pctSum;
}
/** 最终冰冻抗性 = 基础 + 限时修饰(加法模型) */
public getFinalFreezeRes(): number {
const mods = this.aggregateTimedMods(Attrs.freeze_res);
@@ -687,6 +715,8 @@ export class HeroAttrsComp extends ecs.Comp {
this.runtime_field = undefined;
this.critical = 0;
this.critical_res = 0;
this.dodge = 0;
this.accuracy = 0;
this.freeze_chance = 0;
this.freeze_res = 0;
this.stun_chance = 0;

View File

@@ -241,6 +241,11 @@ export class HeroViewComp extends CCComp {
shield_tip(absorbed: number) {
this.hp_tip(TooltipTypes.shield, NumberFormatter.formatNumber(Math.max(0, Math.floor(absorbed))));
}
/** 闪避提示 */
showMiss() {
this.hp_tip(TooltipTypes.miss, "闪避");
}
public playBuff(anm: string = "") {
if (anm === "") return;
var path = "game/skill/buff/" + anm;

View File

@@ -176,6 +176,9 @@ export class Monster extends ecs.Entity {
// TODO: 局外升级机制落地后,此处基础值将与存档强化累加重算
model.defense = Math.round(hero.def * coef);
model.speed = hero.speed ?? 800;
// 闪避/命中:配置优先,缺省取全局默认值(百分点)
model.dodge = hero.dodge ?? FightSet.DEFAULT_DODGE;
model.accuracy = hero.accuracy ?? FightSet.DEFAULT_ACCURACY;
model.type = hero.type;
model.fac = FacSet.MON;
// lv12 职业属性专项:按 11 级当前有效值追加 50%(怪物无局内升级,初始超档直接补齐)

View File

@@ -236,6 +236,7 @@ export class Skill extends ecs.Entity {
sDataCom.Attrs[Attrs.knockback_skill_chance] = config.bck_chance ?? 0;
sDataCom.Attrs[Attrs.puncture_chance] = cAttrsComp.getFinalPunctureChance(); // 初始化携带施法者的穿透概率
sDataCom.Attrs[Attrs.puncture_dmg_bonus] = cAttrsComp.getFinalPunctureDmgBonus(); // 初始化携带施法者的穿透伤害加成
sDataCom.Attrs[Attrs.accuracy] = cAttrsComp.getFinalAccuracy(); // 初始化携带施法者的命中率(含驻场光环与计时 buff)
sDataCom.s_uuid = s_uuid
sDataCom.skill_lv = Math.max(0, skill_lv);
sDataCom.fac = cAttrsComp.fac

View File

@@ -155,6 +155,12 @@ export class TooltipCom extends CCComp {
this.setupLabel("add_life", "hp", "0");
isHeal = true;
break;
case TooltipTypes.miss: // 闪避
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupLabel("add_life", "hp", "闪避");
isHeal = true;
break;
}
this.playAnimation(scaleMax, isCrit, isHeal, sx);