feat(skill): add trigger probability and pity system for attack/attacked triggers
1. 新增技能触发概率配置与保底叠加机制,仅对攻击/受击触发生效 2. 前端编辑器新增t_chance概率与pity_add保底增量配置项 3. 技能描述文案支持展示触发概率 4. 运行时新增概率判定与保底缓存逻辑,兼容存量配置
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvBonusAttrsEntry, FieldEntry, HRole } from "../common/config/heroSet";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvBonusAttrsEntry, FieldEntry, HRole, ResolvedTriggerEntry } from "../common/config/heroSet";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
|
||||
@@ -64,20 +64,26 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
|
||||
// ==================== 运行时解析缓存(按当前等级 resolve 后的有效列表) ====================
|
||||
/** 当前等级下生效的攻击触发条目(供 SkillTriggerHelper 消费) */
|
||||
runtime_atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_atking?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的受击触发条目 */
|
||||
runtime_atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_atked?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的召唤触发条目 */
|
||||
runtime_call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_call?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的死亡触发条目 */
|
||||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_dead?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的战前触发条目 */
|
||||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_fstart?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的回合结束触发条目 */
|
||||
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_fend?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的驻场光环词条列表 */
|
||||
runtime_field?: FieldEntry[];
|
||||
|
||||
// ==================== 触发概率保底缓存(key: s_uuid, value: 已累积的概率增量百分点) ====================
|
||||
/** 攻击触发未命中时累积的下次概率增量(pity_add 驱动,触发成功后清零) */
|
||||
pity_atking: Record<number, number> = {};
|
||||
/** 受击触发未命中时累积的下次概率增量(pity_add 驱动,触发成功后清零) */
|
||||
pity_atked: Record<number, number> = {};
|
||||
|
||||
// ==================== 特殊属性 ====================
|
||||
critical: number = 0; // 暴击率
|
||||
critical_res: number = 0; // 暴击抗性
|
||||
@@ -759,6 +765,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.runtime_fstart = undefined;
|
||||
this.runtime_fend = undefined;
|
||||
this.runtime_field = undefined;
|
||||
// 清空触发概率保底叠加缓存
|
||||
this.pity_atking = {};
|
||||
this.pity_atked = {};
|
||||
this.critical = 0;
|
||||
this.critical_res = 0;
|
||||
this.dodge = 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { ResolvedTriggerEntry, SkillTriggerType } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
@@ -118,7 +118,8 @@ export class SkillTriggerHelper {
|
||||
model.runtime_atking.forEach(atkConfig => {
|
||||
// atk_count 代表已进行的普攻次数。当其余数刚好整除配置阈值时触发。
|
||||
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
|
||||
if (model.atk_count > 0 && model.atk_count % tNum === 0) {
|
||||
if (model.atk_count > 0 && model.atk_count % tNum === 0
|
||||
&& this.rollTriggerChance(model, SkillTriggerType.Atking, atkConfig)) {
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking, atkConfig.overrides);
|
||||
}
|
||||
@@ -149,7 +150,8 @@ export class SkillTriggerHelper {
|
||||
model.runtime_atked.forEach(atkConfig => {
|
||||
// atked_count 代表已承受的受击次数。当其余数刚好整除配置阈值时触发。
|
||||
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
|
||||
if (model.atked_count > 0 && model.atked_count % tNum === 0) {
|
||||
if (model.atked_count > 0 && model.atked_count % tNum === 0
|
||||
&& this.rollTriggerChance(model, SkillTriggerType.Atked, atkConfig)) {
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked, atkConfig.overrides);
|
||||
}
|
||||
@@ -157,6 +159,31 @@ export class SkillTriggerHelper {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发概率判定 + 保底叠加(仅 Atking/Atked 消费)
|
||||
* 次数条件达成后按 t_chance(缺省 100 必触发,兼容存量配置)掷骰;
|
||||
* 未命中且配置 pity_add > 0 时,为该 s_uuid 累积下次概率增量,形成"越不触发越容易触发"的保底体验;
|
||||
* 触发成功后清空叠加,最终概率封顶 100。
|
||||
*
|
||||
* @param model 施法者属性组件(保底缓存挂载于此)
|
||||
* @param type 触发类型(决定读/写哪份保底缓存)
|
||||
* @param config 当前生效的触发条目
|
||||
* @returns 是否通过概率判定
|
||||
*/
|
||||
private static rollTriggerChance(model: HeroAttrsComp, type: SkillTriggerType, config: ResolvedTriggerEntry): boolean {
|
||||
const pityMap = type === SkillTriggerType.Atking ? model.pity_atking : model.pity_atked;
|
||||
const chance = Math.min(100, (config.t_chance ?? 100) + (pityMap[config.s_uuid] ?? 0));
|
||||
if (chance >= 100 || Math.random() * 100 < chance) {
|
||||
pityMap[config.s_uuid] = 0; // 触发成功:清空保底叠加
|
||||
return true;
|
||||
}
|
||||
const pityAdd = config.pity_add ?? 0;
|
||||
if (pityAdd > 0) {
|
||||
pityMap[config.s_uuid] = (pityMap[config.s_uuid] ?? 0) + pityAdd;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user