From 1dbb3bd90d8f1afddb7e316a5df36682e27ffafd Mon Sep 17 00:00:00 2001 From: panFD Date: Tue, 1 Sep 2026 20:47:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(hero):=20=E9=87=8D=E6=9E=84=E8=8B=B1?= =?UTF-8?q?=E9=9B=84=E6=95=B0=E5=80=BC=E7=B3=BB=E7=BB=9F=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=93=81=E8=B4=A8=E7=B3=BB=E6=95=B0=E4=B8=8E12?= =?UTF-8?q?=E7=BA=A7=E8=81=8C=E4=B8=9A=E5=B1=9E=E6=80=A7=E5=8A=A0=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 调整英雄最大等级为99,实现近似无限升级 2. 新增防御属性加成系统,补充def_bonus相关配置与计算 3. 引入英雄品质系数机制,相邻品质基础属性相差5% 4. 新增12级职业属性专项加成:坦克加防、战士辅助加血、输出加攻 5. 更新英雄设计文档与配置示例,统一数值计算逻辑 6. 重构成长计算函数,支持防御属性成长与加成 --- assets/script/game/common/config/GameSet.ts | 4 +- assets/script/game/common/config/heroSet.ts | 179 +++++++++++++----- .../script/game/common/config/英雄设计指引.md | 9 +- assets/script/game/hero/Hero.ts | 31 ++- assets/script/game/hero/HeroAttrsComp.ts | 4 +- assets/script/game/map/MissionCardComp.ts | 26 ++- 6 files changed, 185 insertions(+), 68 deletions(-) diff --git a/assets/script/game/common/config/GameSet.ts b/assets/script/game/common/config/GameSet.ts index 2e043802..d2e49075 100644 --- a/assets/script/game/common/config/GameSet.ts +++ b/assets/script/game/common/config/GameSet.ts @@ -40,8 +40,8 @@ export enum FightSet { HEARTPOS = -320,//基地位置 /** 场上英雄编制数:由开局 3 次三选一固定,玩家无法扩充 */ HERO_FIELD_NUM = 3, - /** 英雄可升级到的最高等级(通过升级卡提升) */ - HERO_MAX_LV = 6, + /** 英雄可升级到的最高等级(通过升级卡提升),99 近似无限升级 */ + HERO_MAX_LV = 99, /** 英雄技能等级上限(无限升级下技能 5 级封顶,超出等级只长属性) */ HERO_SKILL_MAX_LV = 5, /** 英雄属性随等级成长的倍率(ap/hp = base * MULTIPLIER^(lv-1)) */ diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index a9d45734..5b7fdce9 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -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" @@ -107,15 +107,37 @@ export enum HGrowType { } /** 成长类型系数表(每级增量 = 英雄基础属性 × 对应系数) */ -export const HERO_GROW_COEF: Record = { - [HGrowType.Tank]: { hp: 1.5, ap: 0.6 }, - [HGrowType.Warrior]: { hp: 1.0, ap: 1.0 }, - [HGrowType.Assassin]: { hp: 0.7, ap: 1.4 }, - [HGrowType.Mage]: { hp: 0.9, ap: 1.3 }, - [HGrowType.Support]: { hp: 1.1, ap: 0.7 }, - [HGrowType.Archer]: { hp: 0.8, ap: 1.3 }, +export const HERO_GROW_COEF: Record = { + [HGrowType.Tank]: { hp: 1.5, ap: 0.6, def: 1.5 }, + [HGrowType.Warrior]: { hp: 1.0, ap: 1.0, def: 1.0 }, + [HGrowType.Assassin]: { hp: 0.7, ap: 1.4, def: 0.5 }, + [HGrowType.Mage]: { hp: 0.9, ap: 1.3, def: 0.5 }, + [HGrowType.Support]: { hp: 1.1, ap: 0.7, def: 1.0 }, + [HGrowType.Archer]: { hp: 0.8, ap: 1.3, def: 0.5 }, }; +/** + * 英雄品质系数步长:相邻品质(card_lv)基础属性相差 5%。 + * 系数 = 1 + HERO_QUALITY_STEP × (card_lv - 1),lv1~lv5 → 1.00/1.05/1.10/1.15/1.20。 + * 仅作用于 lv1 初始基础属性(AP/HP/DEF),成长与等级 bonus 不随品质缩放。 + */ +export const HERO_QUALITY_STEP = 0.05; + +/** 计算英雄品质系数(相邻品质基础属性差 5%) */ +export function getHeroQualityCoef(cardLv: number): number { + return 1 + HERO_QUALITY_STEP * (Math.max(1, cardLv) - 1); +} + +/** + * lv12 职业属性专项:英雄等级达到 12 级时,按职业对单项基础属性 +50%。 + * 基数为 lv11 当前等级有效值(基础 + 成长 + lv9 bonus,不含 lv12 自身)。 + * Why: 坦克堆防、战士/辅助堆血、输出位堆攻,强化职业特色。 + */ +export const HERO_LV12_SPECIAL_LV = 12; + +/** lv12 专项提升的档位等级(基数取 LV12 - 1 = 11 级有效值) */ +export const HERO_LV12_BASE_LV = HERO_LV12_SPECIAL_LV - 1; + /** * 英雄位置类型(职业定位)。 * Why: grow_type 仅驱动数值成长曲线,无法表达"这个英雄在阵容里干什么"; @@ -139,6 +161,16 @@ export const HRoleName: Record = { [HRole.Support]: "辅助", }; +/** lv12 各职业专项属性映射(坦克堆防、战士/辅助堆血、输出位堆攻) */ +export const HERO_LV12_SPECIAL_ATTR: Record = { + [HRole.Tank]: "def", + [HRole.Warrior]: "hp", + [HRole.Assassin]: "ap", + [HRole.Archer]: "ap", + [HRole.Mage]: "ap", + [HRole.Support]: "hp", +}; + /** * 职业基础属性模板(仅英雄使用,怪物无 role 直接手写 hp/ap/def)。 * 英雄配置处的 hp/ap/def = 本模板值 + 该卡手动增量,模板保证同职业基准一致、增量表达个体差异。 @@ -158,16 +190,17 @@ export const HERO_ROLE_BASE: Record, targetLv: number): { hp: number; ap: number } { +export function calcGrowBonus(hero: Pick, targetLv: number): { hp: number; ap: number; def: number } { const coef = HERO_GROW_COEF[hero.grow_type ?? HGrowType.Warrior]; const steps = Math.max(0, Math.floor(targetLv) - 1); return { hp: Math.floor(hero.hp * coef.hp * steps), ap: Math.floor(hero.ap * coef.ap * steps), + def: Math.floor(hero.def * coef.def * steps), }; } @@ -295,6 +328,7 @@ export interface heroInfo { skills: Record; // 携带技能ID列表 ap_bonus?: LvBonusEntry[]; // 额外攻击加成(按 lv 升序,运行时累加 ≤英雄等级 的所有档) hp_bonus?: LvBonusEntry[]; // 额外生命加成(按 lv 升序,运行时累加 ≤英雄等级 的所有档) + def_bonus?: LvBonusEntry[]; // 额外防御加成(按 lv 升序,运行时累加 ≤英雄等级 的所有档) info: string; // 描述文案 /** * @deprecated 已废弃:无任何 UI/运行时读取。技能提升说明由 HeroSkillText 依据 @@ -422,8 +456,10 @@ export interface LvBonusEntry { *=============英雄配置列表================ * 几何王国复刻版 — 依据 英雄设计指引.md(A/B 级复刻英雄)配置 * - * 规则:lv3 解锁 atking/atked 触发技能,lv2 解锁 fstart,lv6/12 大招走 lv_entries 分档成长, - * lv9 card_lv 4/5 配 field 光环,card_lv 3 配 ap_bonus/hp_bonus 基础属性+50% + * 规则:lv1 激活 skills[0]/skills[1],lv6 解锁 atking/atked/fstart 触发技能, + * 大招(skills[1])lv9/lv16 走 lv_entries 分档成长,lv12 按职业对单项基础属性 +50%(基数取 lv11 有效值), + * lv9 card_lv 4/5 配 field 光环,card_lv 3 配 ap_bonus/hp_bonus 基础属性+50%, + * 品质(card_lv)相邻基础属性差 5%(仅 lv1 初始 AP/HP/DEF) * 编号:本批复刻英雄集中于 5001~5009,card_lv 按配置(不遵循段位规则) */ @@ -443,14 +479,14 @@ export const HeroInfo: Record = { overrides: { ap: 250, bck: 30, para: 40, summon_count: 1 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,击退{bck},{para}%概率麻痹", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 250, bck: 60, para: 40, summon_count: 1 } }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 300, bck: 90, timed_buff_id: 7005, para: 50, summon_count: 2 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,击退{bck},{para}%概率麻痹,额外释放1次,并使目标中毒(每0.5秒受到{buff_value}点毒伤,持续{dur}秒)" }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 250, bck: 60, para: 40, summon_count: 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 300, bck: 90, timed_buff_id: 7005, para: 50, summon_count: 2 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,击退{bck},{para}%概率麻痹,额外释放1次,并使目标中毒(每0.5秒受到{buff_value}点毒伤,持续{dur}秒)" }, ], }, }, atking: { 6010: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "雷霆万钧", t_num: 3, overrides: { para: 100 }, desc: "释放紫魔球轰击{target},{para}%概率麻痹目标" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "雷霆万钧", t_num: 3, overrides: { para: 100 }, desc: "释放紫魔球轰击{target},{para}%概率麻痹目标" }, ], }, field: [ @@ -469,14 +505,14 @@ export const HeroInfo: Record = { overrides: { ap: 150 }, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7007 }, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 180, hit_count: 8, num: 1 }, desc: "斩出光闪,对{target}连击{hit_count}次,每次造成攻击力{ap}%伤害,并双发{num}次" }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7007 }, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 180, hit_count: 8, num: 1 }, desc: "斩出光闪,对{target}连击{hit_count}次,每次造成攻击力{ap}%伤害,并双发{num}次" }, ], }, }, atking: { 6004: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "致命节奏", t_num: 3, overrides: { crt: 30 }, desc: "释放斩击,{crt}%概率造成暴击" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "致命节奏", t_num: 3, overrides: { crt: 30 }, desc: "释放斩击,{crt}%概率造成暴击" }, ], }, field: [ @@ -495,19 +531,19 @@ export const HeroInfo: Record = { overrides: { ap: 150, para: 40, summon_count: 1 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,{para}%概率麻痹", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7006, para: 40, summon_count: 1 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,{para}%概率麻痹,并使目标攻击力{buff_value}%(持续{dur}秒)" }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 180, timed_buff_id: 7006, buff_value: -15, para: 50, summon_count: 2 } }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7006, para: 40, summon_count: 1 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,{para}%概率麻痹,并使目标攻击力{buff_value}%(持续{dur}秒)" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 180, timed_buff_id: 7006, buff_value: -15, para: 50, summon_count: 2 } }, ], }, }, atked: { 6301: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "罗生门", t_num: 4, overrides: { TGroup: TGroup.Self, ap: 2 }, desc: "{target}获得{ap}次格挡护盾" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "罗生门", t_num: 4, overrides: { TGroup: TGroup.Self, ap: 2 }, desc: "{target}获得{ap}次格挡护盾" }, ], }, fstart: { 6200: [ - { lv: 2, s_lv: SkillLv.Lv1, name: "三十六烦恼凤", t_num: 1, overrides: { ap: 500 }, desc: "入场释放剑气,对{target}造成攻击力{ap}%伤害" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "三十六烦恼凤", t_num: 1, overrides: { ap: 500 }, desc: "入场释放剑气,对{target}造成攻击力{ap}%伤害" }, ], }, field: [ @@ -526,14 +562,14 @@ export const HeroInfo: Record = { overrides: { ap: 150, summon_count: 1 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 150, frz: 15, summon_count: 1 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻" }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 180, frz: 30, hit_count: 8, summon_count: 2 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻,命中{hit_count}次" }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 150, frz: 15, summon_count: 1 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 180, frz: 30, hit_count: 8, summon_count: 2 }, desc: "释放能量波,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻,命中{hit_count}次" }, ], }, }, atking: { 6009: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "霜寒之触", t_num: 3, overrides: { timed_buff_id: 7003 }, desc: "释放寒气,冰冻{target}{dur}秒" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "霜寒之触", t_num: 3, overrides: { timed_buff_id: 7003 }, desc: "释放寒气,冰冻{target}{dur}秒" }, ], }, field: [ @@ -552,19 +588,19 @@ export const HeroInfo: Record = { overrides: { TGroup: TGroup.Self, ap: 3, summon_count: 1 }, desc: "{target}获得{ap}次格挡护盾", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { TGroup: TGroup.Self, ap: 5, summon_count: 1 } }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { TGroup: TGroup.Self, ap: 6, summon_count: 2 } }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { TGroup: TGroup.Self, ap: 5, summon_count: 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { TGroup: TGroup.Self, ap: 6, summon_count: 2 } }, ], }, }, atked: { 6004: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "反击减攻", t_num: 4, overrides: { timed_buff_id: 7006 }, desc: "反击使攻击者攻击力{buff_value}%,持续{dur}秒" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "反击减攻", t_num: 4, overrides: { timed_buff_id: 7006 }, desc: "反击使攻击者攻击力{buff_value}%,持续{dur}秒" }, ], }, fstart: { 6412: [ - { lv: 2, s_lv: SkillLv.Lv1, name: "铁壁壁垒", t_num: 1, overrides: { TGroup: TGroup.Team, ap: 50 }, desc: "{target}防御提升{ap}点" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "铁壁壁垒", t_num: 1, overrides: { TGroup: TGroup.Team, ap: 50 }, desc: "{target}防御提升{ap}点" }, ], }, dead: { @@ -592,19 +628,19 @@ export const HeroInfo: Record = { overrides: { TGroup: TGroup.Team, ap: 300, summon_count: 1 }, desc: "为{target}回复攻击力{ap}%生命", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { TGroup: TGroup.Team, ap: 400, summon_count: 1 } }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { TGroup: TGroup.Team, ap: 450, summon_count: 2 } }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { TGroup: TGroup.Team, ap: 400, summon_count: 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { TGroup: TGroup.Team, ap: 450, summon_count: 2 } }, ], }, }, atking: { 6302: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "圣洁之锤", t_num: 3, overrides: { TGroup: TGroup.Self, ap: 100 }, desc: "释放圣光,为{target}回复攻击力{ap}%生命" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "圣洁之锤", t_num: 3, overrides: { TGroup: TGroup.Self, ap: 100 }, desc: "释放圣光,为{target}回复攻击力{ap}%生命" }, ], }, fstart: { 6412: [ - { lv: 2, s_lv: SkillLv.Lv1, name: "圣光庇佑", t_num: 1, overrides: { TGroup: TGroup.Team, ap: 30 }, desc: "{target}防御提升{ap}点" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "圣光庇佑", t_num: 1, overrides: { TGroup: TGroup.Team, ap: 30 }, desc: "{target}防御提升{ap}点" }, ], }, field: [ @@ -623,14 +659,14 @@ export const HeroInfo: Record = { overrides: { ap: 150, summon_count: 1 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7002, summon_count: 1 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)" }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 180, bck: 60, timed_buff_id: 7002, buff_value: 20, summon_count: 2 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,击退{bck},并使目标易伤(持续{dur}秒)" }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 150, timed_buff_id: 7002, summon_count: 1 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 180, bck: 60, timed_buff_id: 7002, buff_value: 20, summon_count: 2 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,击退{bck},并使目标易伤(持续{dur}秒)" }, ], }, }, atking: { 6004: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "雷霆一击", t_num: 3, overrides: { bck: 40 }, desc: "重击{target},击退{bck}" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "雷霆一击", t_num: 3, overrides: { bck: 40 }, desc: "重击{target},击退{bck}" }, ], }, // card_lv 3:lv9 无光环,改为基础属性+50% @@ -649,14 +685,14 @@ export const HeroInfo: Record = { overrides: { ap: 300, summon_count: 1 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 300, stun: 20, summon_count: 1 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,{stun}%概率击晕" }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 350, stun: 35, hit_count: 8, summon_count: 2 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,{stun}%概率击晕,命中{hit_count}次" }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 300, stun: 20, summon_count: 1 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,{stun}%概率击晕" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 350, stun: 35, hit_count: 8, summon_count: 2 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,{stun}%概率击晕,命中{hit_count}次" }, ], }, }, atking: { 6004: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "火眼金睛", t_num: 3, overrides: { crt: 25 }, desc: "释放斩击,{crt}%概率造成暴击" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "火眼金睛", t_num: 3, overrides: { crt: 25 }, desc: "释放斩击,{crt}%概率造成暴击" }, ], }, field: [ @@ -675,20 +711,20 @@ export const HeroInfo: Record = { overrides: { ap: 150, summon_count: 1 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 6, s_lv: SkillLv.Lv2, overrides: { ap: 180, summon_count: 1 } }, - { lv: 12, s_lv: SkillLv.Lv3, overrides: { ap: 200, summon_count: 2 } }, + { lv: 9, s_lv: SkillLv.Lv2, overrides: { ap: 180, summon_count: 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { ap: 200, summon_count: 2 } }, ], }, }, atking: { 6302: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "嗜血战斧", t_num: 4, overrides: { TGroup: TGroup.Self, ap: 120 }, desc: "释放嗜血斩击,为{target}回复攻击力{ap}%生命" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "嗜血战斧", t_num: 4, overrides: { TGroup: TGroup.Self, ap: 120 }, desc: "释放嗜血斩击,为{target}回复攻击力{ap}%生命" }, ], }, atked: { // 坚硬皮肤:借 6513 防御爆发底座注入 7023 防御强化(+50%,可无限叠层) 6513: [ - { lv: 3, s_lv: SkillLv.Lv1, name: "坚硬皮肤", t_num: 2, overrides: { TGroup: TGroup.Self, timed_buff_id: 7023, buff_duration: 10 }, desc: "{target}防御提升{buff_value}%,持续{dur}秒" }, + { lv: 6, s_lv: SkillLv.Lv1, name: "坚硬皮肤", t_num: 2, overrides: { TGroup: TGroup.Self, timed_buff_id: 7023, buff_duration: 10 }, desc: "{target}防御提升{buff_value}%,持续{dur}秒" }, ], }, // card_lv 3:lv9 无光环,改为基础属性+50% @@ -964,13 +1000,14 @@ export function resolveReviveByLv(revive: LvReviveEntry[] | undefined, lv: numbe return pick ? { s_uuid: pick.s_uuid, r_num: pick.r_num, upr: pick.upr } : undefined; } -/** 计算目标等级下额外属性加成(hp_bonus/ap_bonus 一次性奖励 + 成长类型每级递增) */ -function getEvolveBonuses(hero: heroInfo, targetLv: number): { hp: number, ap: number } { +/** 计算目标等级下额外属性加成(hp/ap/def_bonus 一次性奖励 + 成长类型每级递增) */ +function getEvolveBonuses(hero: heroInfo, targetLv: number): { hp: number, ap: number, def: number } { const lv = Math.max(1, targetLv); // 成长类型线性增量(仅英雄生效,怪物无 grow_type 时走 Warrior 基准但不影响平衡,因怪物通常不填) - const grow = hero.fac === FacSet.HERO ? calcGrowBonus(hero, lv) : { hp: 0, ap: 0 }; + const grow = hero.fac === FacSet.HERO ? calcGrowBonus(hero, lv) : { hp: 0, ap: 0, def: 0 }; let hp = grow.hp; let ap = grow.ap; + let def = grow.def; if (hero.hp_bonus) { for (const e of hero.hp_bonus) { if (e.lv <= lv) hp += e.value; @@ -981,15 +1018,57 @@ function getEvolveBonuses(hero: heroInfo, targetLv: number): { hp: number, ap: n if (e.lv <= lv) ap += e.value; } } - return { hp, ap }; + if (hero.def_bonus) { + for (const e of hero.def_bonus) { + if (e.lv <= lv) def += e.value; + } + } + return { hp, ap, def }; } -/** 计算目标等级下的有效 HP/AP(固定基础值 + 成长递增与一次性加成) */ -function getEffectiveStats(hero: heroInfo, targetLv: number): { hp: number, ap: number } { +/** 计算目标等级下的有效 HP/AP/DEF(固定基础值 + 成长递增与一次性加成) */ +export function getEffectiveStats(hero: heroInfo, targetLv: number): { hp: number, ap: number, def: number } { const bonuses = getEvolveBonuses(hero, targetLv); return { hp: hero.hp + bonuses.hp, ap: hero.ap + bonuses.ap, + def: hero.def + bonuses.def, + }; +} + +/** + * 计算英雄展示用最终属性(含品质系数与 lv12 职业专项),供 UI 面板与运行时对齐口径。 + * 公式:基础值 × 品质系数 + 成长 + bonus,再叠加 lv12 专项(基数取 lv11 有效值)。 + * @param hero 英雄配置 + * @param targetLv 目标等级(≥1) + * @returns 含品质与专项的最终 hp/ap/def(已取整) + */ +export function getHeroDisplayStats(hero: heroInfo, targetLv: number): { hp: number, ap: number, def: number } { + const lv = Math.max(1, Math.floor(targetLv)); + const coef = getHeroQualityCoef(hero.card_lv ?? 1); + const eff = getEffectiveStats(hero, lv); + const stats = { + hp: Math.round(hero.hp * coef) + (eff.hp - hero.hp), + ap: Math.round(hero.ap * coef) + (eff.ap - hero.ap), + def: Math.round(hero.def * coef) + (eff.def - hero.def), + }; + // lv12 职业专项:基数为 lv11 展示有效值 + if (lv >= HERO_LV12_SPECIAL_LV && hero.role !== undefined) { + const base11 = getHeroDisplayStatsBase(hero, HERO_LV12_BASE_LV); + const attr = HERO_LV12_SPECIAL_ATTR[hero.role]; + stats[attr] += Math.round(base11[attr] * 0.5); + } + return stats; +} + +/** 内部:lv12 专项基数(不含 lv12 自身)的展示有效值 */ +function getHeroDisplayStatsBase(hero: heroInfo, targetLv: number): { hp: number, ap: number, def: number } { + const coef = getHeroQualityCoef(hero.card_lv ?? 1); + const eff = getEffectiveStats(hero, targetLv); + return { + hp: Math.round(hero.hp * coef) + (eff.hp - hero.hp), + ap: Math.round(hero.ap * coef) + (eff.ap - hero.ap), + def: Math.round(hero.def * coef) + (eff.def - hero.def), }; } diff --git a/assets/script/game/common/config/英雄设计指引.md b/assets/script/game/common/config/英雄设计指引.md index ff6f8580..5af627e9 100644 --- a/assets/script/game/common/config/英雄设计指引.md +++ b/assets/script/game/common/config/英雄设计指引.md @@ -49,7 +49,8 @@ revive[0]:无 **通用约束**: -- lv2 走 atking/atked/fstart 提升普攻能力;lv3/5/7 走 `skills[1].lv_entries` 提升大招;lv9 光环或属性补偿 +- 等级节奏:lv1 激活 skills[0]/skills[1];lv6 解锁 atking/atked/fstart 触发技能(1 个);大招(skills[1])lv9/lv16 走 `lv_entries` 分档提升;lv12 按职业对单项基础属性 +50%(基数取 lv11 当前等级有效值:坦克→def、战士/辅助→hp、刺客/射手/法师→ap);lv9 光环或属性补偿 +- 品质差异:相邻品质(card_lv)lv1 初始基础属性(AP/HP/DEF)相差 5%(系数 1+0.05×(card_lv-1)),其余配置参数完全一致 - 能力只能从能力池(BuffList 7001\~7024 / FieldSkillSet 7001\~7020 / SkillSet 底座)引用 + `overrides` 数值覆盖,**禁止新增同义底座** **大招召唤数约束**(`summon_count` 覆写规则): @@ -303,11 +304,13 @@ revive[0]:无 ## 配置检查清单 - [ ] `skills` 引用的 uuid 在 SkillSet 中存在,`overrides.ap` 显式写出 -- [ ] 大招(`skills[1]`)覆写 `summon_count`:lv1=1、lv3=1、lv5=2、lv7=3,通过 `lv_entries` 逐级覆写,不改动 SkillSet 底座 +- [ ] 大招(`skills[1]`)覆写 `summon_count`:lv1=1、lv9=1、lv16=2,通过 `lv_entries` 逐级覆写,不改动 SkillSet 底座 - [ ] `timed_buff_id` 指向 BuffList 已存在条目(能力池 2.1/2.2 内) - [ ] debuff/buff 只通过 `overrides` 注入,未改动 SkillSet/BuffList 底座 -- [ ] `atking`/`atked`/`fstart`/`dead` 按 s\_uuid 分组、按 lv 升序 +- [ ] `atking`/`atked`/`fstart`/`dead` 按 s\_uuid 分组、按 lv 升序,触发技能统一 lv6 解锁 - [ ] lv9:card\_lv 4/5 配 `field`;card\_lv 1/2/3 配 `ap_bonus`/`hp_bonus`(30/40/50%) +- [ ] lv12:按职业注入单项属性 +50%(坦克 def、战士/辅助 hp、刺客/射手/法师 ap),基数取 lv11 有效值 +- [ ] 品质:card\_lv 相邻基础属性差 5%(运行时品质系数统一乘,配置保持裸值) - [ ] `t_num` 概率模拟符合预期(1=每次,3≈33%,4≈25%) - [ ] `infos` 分级说明与配置档位一一对应(面板逐条展示) - [ ] 近战/远程形态与 `type`/`dis` 匹配,美术资源 `path`/`icon` 就绪 diff --git a/assets/script/game/hero/Hero.ts b/assets/script/game/hero/Hero.ts index 92c40b9b..6e1e395e 100644 --- a/assets/script/game/hero/Hero.ts +++ b/assets/script/game/hero/Hero.ts @@ -5,7 +5,7 @@ import { smc } from "../common/SingletonModuleComp"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroViewComp } from "./HeroViewComp"; import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet"; -import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus, HeroDisVal } from "../common/config/heroSet"; +import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus, HeroDisVal, getHeroQualityCoef, getEffectiveStats, HERO_LV12_SPECIAL_LV, HERO_LV12_BASE_LV, HERO_LV12_SPECIAL_ATTR } from "../common/config/heroSet"; import { GameEvent } from "../common/config/GameEvent"; import { SkillTriggerType, HType } from "../common/config/heroSet"; import { SkillTriggerHelper } from "./SkillTriggerHelper"; @@ -140,15 +140,18 @@ export class Hero extends ecs.Entity { model.revive = hero.revive; model.ap_bonus = hero.ap_bonus; model.hp_bonus = hero.hp_bonus; + model.def_bonus = hero.def_bonus; - // 基础属性:固定基础值,成长完全由 grow_type 指数递增提供(替代旧 ×2^(lv-1) 翻倍) + // 基础属性:固定基础值 × 品质系数(相邻品质差 5%),成长由 grow_type 线性递增提供 // TODO: 局外升级机制落地后,此处基础值将与存档强化累加重算 - let base_ap = hero.ap; - let base_hp = hero.hp; + const qualityCoef = getHeroQualityCoef(hero.card_lv ?? 1); + let base_ap = Math.round(hero.ap * qualityCoef); + let base_hp = Math.round(hero.hp * qualityCoef); + let base_def = Math.round(hero.def * qualityCoef); model.base_ap = base_ap; model.base_hp = base_hp; - model.defense = hero.def; + model.defense = base_def; // 英雄享受驻场百分比加成,怪物保持基础值 if (model.fac === FacSet.HERO) { @@ -179,11 +182,12 @@ export class Hero extends ecs.Entity { model.runtime_field = resolveFieldByLv(model.field, hero_lv); model.runtime_revive = resolveReviveByLv(model.revive, hero_lv); - // 应用额外属性加成(成长类型每级递增 + hp_bonus/ap_bonus 一次性奖励累加) + // 应用额外属性加成(成长类型每级递增 + hp/ap/def_bonus 一次性奖励累加) // 成长增量:仅英雄生效(model.fac 已为 HERO),按 grow_type 递增曲线累计 const grow = calcGrowBonus(hero, hero_lv); model.hp += grow.hp; model.hp_max += grow.hp; model.ap += grow.ap; + model.defense += grow.def; if (model.hp_bonus) { let hpExtra = 0; for (const e of model.hp_bonus) { if (e.lv <= hero_lv) hpExtra += e.value; } @@ -194,6 +198,21 @@ export class Hero extends ecs.Entity { for (const e of model.ap_bonus) { if (e.lv <= hero_lv) apExtra += e.value; } model.ap += apExtra; } + if (model.def_bonus) { + let defExtra = 0; + for (const e of model.def_bonus) { if (e.lv <= hero_lv) defExtra += e.value; } + model.defense += defExtra; + } + + // lv12 职业属性专项:达到 12 级时按职业对单项基础属性 +50% + // 基数为 lv11 当前等级有效值(基础 + 成长 + lv9 bonus,不含 lv12 自身),仅英雄生效 + if (model.fac === FacSet.HERO && hero_lv >= HERO_LV12_SPECIAL_LV && model.role !== undefined) { + const lv11 = getEffectiveStats(hero, HERO_LV12_BASE_LV); + const attr = HERO_LV12_SPECIAL_ATTR[model.role]; + if (attr === "def") model.defense += Math.round(lv11.def * 0.5); + else if (attr === "hp") { const add = Math.round(lv11.hp * 0.5); model.hp += add; model.hp_max += add; } + else if (attr === "ap") model.ap += Math.round(lv11.ap * 0.5); + } // 天赋加成层:全局天赋(一阶基础/三阶超强全局)全局生效,职业天赋(二阶专精/三阶超强职业)按 role 过滤(仅英雄生效,怪物跳过) // Why: 放在成长/bonus 之后、光环乘区之后,以基础快照为基数叠加,避免与乘区叠加成乘法膨胀 diff --git a/assets/script/game/hero/HeroAttrsComp.ts b/assets/script/game/hero/HeroAttrsComp.ts index cadc9db2..e711f973 100644 --- a/assets/script/game/hero/HeroAttrsComp.ts +++ b/assets/script/game/hero/HeroAttrsComp.ts @@ -54,9 +54,10 @@ export class HeroAttrsComp extends ecs.Comp { /** 驻场光环(按 lv 解锁,存活期间全局生效) */ [SkillTriggerType.Field]?: LvFieldEntry[]; [SkillTriggerType.Revive]?: LvReviveEntry[]; - /** 额外攻击/生命加成(按 lv 累加) */ + /** 额外攻击/生命/防御加成(按 lv 累加) */ ap_bonus?: LvBonusEntry[]; hp_bonus?: LvBonusEntry[]; + def_bonus?: LvBonusEntry[]; // ==================== 运行时解析缓存(按当前等级 resolve 后的有效列表) ==================== /** 当前等级下生效的攻击触发条目(供 SkillTriggerHelper 消费) */ @@ -633,6 +634,7 @@ export class HeroAttrsComp extends ecs.Comp { this.revive = undefined; this.ap_bonus = undefined; this.hp_bonus = undefined; + this.def_bonus = undefined; this.runtime_atking = undefined; this.runtime_atked = undefined; this.runtime_call = undefined; diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 69a8c42e..12ed8f70 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -54,7 +54,7 @@ import { MissSkillsComp } from "./MissSkillsComp"; import { oops } from "db://oops-framework/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { smc } from "../common/SingletonModuleComp"; -import { HeroInfo, HType, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus } from "../common/config/heroSet"; +import { HeroInfo, HType, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus, getHeroQualityCoef, getEffectiveStats, HERO_LV12_SPECIAL_LV, HERO_LV12_BASE_LV, HERO_LV12_SPECIAL_ATTR } from "../common/config/heroSet"; import { applyTalentBonuses } from "../common/config/TalentSet"; import { HeroViewComp } from "../hero/HeroViewComp"; import { FacSet, FightSet } from "../common/config/GameSet"; @@ -2151,9 +2151,10 @@ export class MissionCardComp extends CCComp { const hpRate = model.hp_max > 0 ? model.hp / model.hp_max : 1; model.lv = nextLv; - // 同步 base 快照(与 Hero.load 同公式:固定基础值,成长由 grow_type 指数递增提供) - const base_ap = hero.ap; - const base_hp = hero.hp; + // 同步 base 快照(与 Hero.load 同公式:固定基础值 × 品质系数,成长由 grow_type 线性递增提供) + const qualityCoef = getHeroQualityCoef(hero.card_lv ?? 1); + const base_ap = Math.round(hero.ap * qualityCoef); + const base_hp = Math.round(hero.hp * qualityCoef); model.base_ap = base_ap; model.base_hp = base_hp; @@ -2166,7 +2167,7 @@ export class MissionCardComp extends CCComp { model.hp_max = base_hp; } - // 按 ≤nextLv 累加:成长类型每级递增 + hp_bonus/ap_bonus 一次性奖励(沿用 Hero.load 的加法区规则) + // 按 ≤nextLv 累加:成长类型每级递增 + hp/ap/def_bonus 一次性奖励(沿用 Hero.load 的加法区规则) const grow = calcGrowBonus(hero, nextLv); model.hp_max += grow.hp; model.ap += grow.ap; @@ -2184,7 +2185,20 @@ export class MissionCardComp extends CCComp { // 天赋加成层(与 Hero.load 对齐):先重置天赋可能修改的字段,避免升级重算时重复叠加 // Why: ap/hp_max 已由上方公式从 base 重算(天然幂等),但 defense 与概率类字段是累加语义,必须先归零再注入 if (model.fac === FacSet.HERO) { - model.defense = hero.def; + // defense 从品质系数后的基础值重算,叠加 def 成长 + def_bonus + lv12 专项,幂等 + let base_def = Math.round(hero.def * qualityCoef) + grow.def; + if (model.def_bonus) { + for (const e of model.def_bonus) { if (e.lv <= nextLv) base_def += e.value; } + } + // lv12 职业属性专项:达到 12 级时按职业对单项基础属性 +50%(基数为 lv11 有效值) + if (nextLv >= HERO_LV12_SPECIAL_LV && model.role !== undefined) { + const lv11 = getEffectiveStats(hero, HERO_LV12_BASE_LV); + const attr = HERO_LV12_SPECIAL_ATTR[model.role]; + if (attr === "def") base_def += Math.round(lv11.def * 0.5); + else if (attr === "hp") model.hp_max += Math.round(lv11.hp * 0.5); + else if (attr === "ap") model.ap += Math.round(lv11.ap * 0.5); + } + model.defense = base_def; model.critical = 0; model.critical_damage = 0; model.stun_chance = 0;