4 Commits

Author SHA1 Message Date
03a8a41980 feat(战斗系统): 添加伤害比例属性并应用到伤害计算
在SDataCom中添加dmg_ratio属性用于调整伤害比例
修改HeroAtkSystem中的伤害计算公式,将dmg_ratio纳入计算
2025-11-24 16:58:13 +08:00
91c18004eb feat(技能系统): 添加额外伤害和溅射伤害功能
在技能组件中新增ext_dmg和splash字段用于处理额外伤害和溅射伤害
修改技能创建和伤害计算逻辑以支持新功能
2025-11-24 16:58:04 +08:00
6df4abadd1 feat(战斗系统): 添加必杀技能类型并优化天赋触发逻辑
- 在TriType枚举中添加MAX类型用于必杀技能触发
- 重构SACastSystem中的技能处理逻辑,将普通攻击、技能和必杀技能分开处理
- 优化天赋触发条件判断,确保不同类型技能触发正确的天赋效果
- 调整技能动画播放和伤害计算逻辑,使风怒和双技能天赋能正确生效
2025-11-24 15:52:28 +08:00
8d2ec76b01 feat(战斗系统): 实现攻击和技能伤害加成天赋效果
添加ATK_DMG和SKILL_DMG天赋类型,支持在普通攻击和技能释放时应用额外伤害
修改SACastSystem以处理不同类型的伤害加成
重构TalEffet枚举并更新相关配置
2025-11-24 15:39:05 +08:00
7 changed files with 106 additions and 50 deletions

View File

@@ -9,28 +9,30 @@ export enum TriType {
ATK = 2, //普通攻击触发 ATK = 2, //普通攻击触发
DMG = 3, // 受伤触发 DMG = 3, // 受伤触发
SKILL = 4, // 技能触发 SKILL = 4, // 技能触发
HPL = 5, // 失去生命值触发 MAX = 5, // 必杀触发
HPA = 6, // 获得生命值触发 HPL = 6, // 失去生命值触发
INIT = 7, // 初始触发,如:多1个技能 HPA = 7, // 获得生命值触发
DEAD = 8 // 基于死亡触发 INIT = 8, // 初始触发,如:多1个技能
DEAD = 9, // 基于死亡触发
} }
export enum TalEffet { export enum TalEffet {
DMG=1, // 伤害 ATK_DMG=1, // 伤害
HP=2, // 回血 百分比 SKILL_DMG=2, // 技能伤害
MP=3, //回蓝 百分比 HP=3, // 回血 百分比
BUFF = 4, // 暴击率,闪避率等,可叠加的触发后清零 MP=4, //回蓝 百分比
STATS=5, // 状态 BUFF = 5, // 暴击率,闪避率等,可叠加的触发后清零
WFUNY=6, // 风怒 STATS=6, // 状态
SPLASH=7, // 溅射 WFUNY=7, // 风怒
D_SKILL=8, //两次技能 SPLASH=8, // 溅射
SHIELD=9, // 护盾 D_SKILL=9, //两次技能
LDMG=10, // 减伤 SHIELD=10, // 护盾
C_ATK=11, // 普工必爆 LDMG=11, // 减伤
C_SKILL=12, // 一般技能必暴 C_ATK=12, // 普工必爆
C_MSKILL=13, // 必杀技能必暴 C_SKILL=13, // 一般技能必暴
C_MSKILL=14, // 必杀技能必暴
} }
export enum TalTarget { export enum TalTarget {
@@ -107,7 +109,7 @@ export const talConf: Record<number, ItalConf> = {
desc:"普通攻击10次后, 下次一般技能额外释放1次,伤害100%"}, desc:"普通攻击10次后, 下次一般技能额外释放1次,伤害100%"},
/*** 受伤触发 ***/ /*** 受伤触发 ***/
7101:{uuid:7101,name:"反击",triType:TriType.DMG,Trigger:3,count:1,target:TalTarget.ENEMY,effet:TalEffet.DMG,vType:BType.RATIO, value:50,attrs:TalAttrs.NON, 7101:{uuid:7101,name:"反击",triType:TriType.DMG,Trigger:3,count:1,target:TalTarget.ENEMY,effet:TalEffet.ATK_DMG,vType:BType.RATIO, value:50,attrs:TalAttrs.NON,
desc:"被攻击3次后, 给于目标50%的伤害"}, desc:"被攻击3次后, 给于目标50%的伤害"},
7102:{uuid:7102,name:"护盾",triType:TriType.DMG,Trigger:3,count:1,target:TalTarget.SELF,effet:TalEffet.SHIELD,vType:BType.RATIO, value:20,attrs:TalAttrs.NON, 7102:{uuid:7102,name:"护盾",triType:TriType.DMG,Trigger:3,count:1,target:TalTarget.SELF,effet:TalEffet.SHIELD,vType:BType.RATIO, value:20,attrs:TalAttrs.NON,
desc:"被攻击3次后, 获得20%的生命值护盾"}, desc:"被攻击3次后, 获得20%的生命值护盾"},

View File

@@ -158,7 +158,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
const isCrit = this.checkChance(damageEvent.Attrs[Attrs.CRITICAL]); const isCrit = this.checkChance(damageEvent.Attrs[Attrs.CRITICAL]);
if (isCrit) attackerModel?.useValueTalByAttr(Attrs.CRITICAL); // 清除施法者的暴击buff if (isCrit) attackerModel?.useValueTalByAttr(Attrs.CRITICAL); // 清除施法者的暴击buff
// 计算伤害 // 计算伤害
let damage = this.dmgCount(damageEvent.Attrs,targetAttrs.Attrs,damageEvent.s_uuid); let damage = this.dmgCount(damageEvent,targetAttrs.Attrs);
if (isCrit) { if (isCrit) {
// 暴击伤害计算 // 暴击伤害计算
// 使用施法者的暴击伤害加成属性damageEvent.Attrs 快照) // 使用施法者的暴击伤害加成属性damageEvent.Attrs 快照)
@@ -228,16 +228,17 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
* - 所有除法和乘法计算后都进行取整操作,确保游戏中的伤害值为整数 * - 所有除法和乘法计算后都进行取整操作,确保游戏中的伤害值为整数
* - 元素伤害只应用于魔法伤害部分 * - 元素伤害只应用于魔法伤害部分
*/ */
private dmgCount(CAttrs:any,TAttrs:any,s_uuid:number){ private dmgCount(damageEvent:any,TAttrs:any){
// 1. 获取技能配置 - 如果技能不存在直接返回0伤害 // 1. 获取技能配置 - 如果技能不存在直接返回0伤害
let sConf = SkillSet[s_uuid]; const CAttrs=damageEvent.Attrs;
let sConf = SkillSet[damageEvent.s_uuid];
if (!sConf) return 0; if (!sConf) return 0;
// 2. 计算原始物理伤害和魔法伤害 // 2. 计算原始物理伤害和魔法伤害
// 物理伤害基础值 = 技能物理倍率 * 施法者物理攻击力 / 100 // 物理伤害基础值 = 技能物理倍率 * (施法者物理攻击力 + 额外物理伤害) / 100 * 伤害比例
let apBase = (sConf.ap||0)*CAttrs[Attrs.AP]/100; let apBase = (sConf.ap||0)*(CAttrs[Attrs.AP]+damageEvent.ext_dmg)/100*damageEvent.dmg_ratio;
// 魔法伤害基础值 = 技能魔法倍率 * 施法者魔法攻击力 / 100 // 魔法伤害基础值 = 技能魔法倍率 * (施法者魔法攻击力 + 额外魔法伤害) / 100 * 伤害比例
let mapBase = (sConf.map||0)*CAttrs[Attrs.MAP]/100; let mapBase = (sConf.map||0)*(CAttrs[Attrs.MAP]+damageEvent.ext_dmg)/100*damageEvent.dmg_ratio;
// 3. 获取目标防御属性 // 3. 获取目标防御属性
const def = (TAttrs[Attrs.DEF]||0); // 目标物理防御 const def = (TAttrs[Attrs.DEF]||0); // 目标物理防御

View File

@@ -433,6 +433,12 @@ export class HeroAttrsComp extends ecs.Comp {
t.count -= 1; t.count -= 1;
return true; return true;
} }
useCountValTal(eff: number): number {
const t = this.Talents[eff];
if (!t || t.value <= 0) return 0;
t.count -= 1;
return t.value;
}
useValueTalByUuid(t_uuid: number) { useValueTalByUuid(t_uuid: number) {
const buff = this.BUFFS_TAL[t_uuid]; const buff = this.BUFFS_TAL[t_uuid];
if (!buff) return; if (!buff) return;

View File

@@ -143,11 +143,12 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
// 1. 播放施法动画 // 1. 播放施法动画
heroView.playSkillEffect(s_uuid); heroView.playSkillEffect(s_uuid);
/**********************天赋处理*************************************************************************/ /**********************天赋处理*************************************************************************/
// 2. 更新攻击类型的天赋触发值 // 2. 更新攻击类型的天赋触发值,技能和必杀级
if(casterEntity.has(TalComp)){ if(casterEntity.has(TalComp)){
const talComp = casterEntity.get(TalComp); const talComp = casterEntity.get(TalComp);
if (hset === HSSet.atk) talComp.updateCur(TriType.ATK); if (hset === HSSet.atk) talComp.updateCur(TriType.ATK);
if (hset != HSSet.atk) talComp.updateCur(TriType.SKILL); if (hset === HSSet.skill) talComp.updateCur(TriType.SKILL);
if (hset === HSSet.max) talComp.updateCur(TriType.MAX);
} }
/**********************天赋处理*************************************************************************/ /**********************天赋处理*************************************************************************/
// 获取目标位置 // 获取目标位置
@@ -156,33 +157,56 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
console.warn("[SACastSystem] 没有找到有效目标"); console.warn("[SACastSystem] 没有找到有效目标");
return false; return false;
} }
// 2. 延迟创建技能实体(等待动画) // 2.1 普通攻击逻辑
const delay = 0.3 if (hset === HSSet.atk){
heroView.scheduleOnce(() => { let delay = 0.3
this.createSkill(s_uuid, heroView,targets); let ext_dmg = heroAttrs.useCountValTal(TalEffet.ATK_DMG);
}, delay); let splash = heroAttrs.useCountValTal(TalEffet.SPLASH);
heroView.scheduleOnce(() => {
//风怒wfuny 只针对 普通攻击起效 this.createSkill(s_uuid, heroView,targets,ext_dmg,splash);
if (hset === HSSet.atk && heroAttrs.useCountTal(TalEffet.WFUNY)){ }, delay);
heroView.playSkillEffect(s_uuid); //风怒wfuny 只针对 普通攻击起效
//需要再添加 风怒动画 if (heroAttrs.useCountTal(TalEffet.WFUNY)){
this.createSkill(s_uuid, heroView,targets); let ext2_dmg = heroAttrs.useCountValTal(TalEffet.ATK_DMG);
} let splash2 = heroAttrs.useCountValTal(TalEffet.SPLASH);
// 双技能 只针对 技能起效 let delay = 0.3
if(hset === HSSet.skill && heroAttrs.useCountTal(TalEffet.D_SKILL)){ heroView.playSkillEffect(s_uuid);
targets = this.sTargets(heroView, s_uuid); //需要再添加 风怒动画
if (targets.length === 0) { heroView.scheduleOnce(() => {
console.warn("[SACastSystem] 没有找到有效目标"); this.createSkill(s_uuid, heroView,targets,ext2_dmg,splash2);
return false; },delay);
} }
}
// 2.2 技能攻击逻辑
if(hset === HSSet.skill){
let delay = 0.3
let ext_dmg = heroAttrs.useCountValTal(TalEffet.SKILL_DMG);
heroView.scheduleOnce(() => {
this.createSkill(s_uuid, heroView,targets,ext_dmg);
}, delay);
// 双技能 只针对 技能起效
if(heroAttrs.useCountTal(TalEffet.D_SKILL)){
let ext2_dmg = heroAttrs.useCountValTal(TalEffet.SKILL_DMG);
let delay = 0.3
heroView.playSkillEffect(s_uuid);
//需要再添加 双技能动画
heroView.scheduleOnce(() => {
this.createSkill(s_uuid, heroView,targets,ext2_dmg);
},delay);
}
}
// 2.3 必杀技能逻辑
if(hset === HSSet.max){
let delay = 0.3
heroView.playSkillEffect(s_uuid); heroView.playSkillEffect(s_uuid);
//需要再添加 双技能动画 //需要再添加 最大伤害动画
heroView.scheduleOnce(() => { heroView.scheduleOnce(() => {
this.createSkill(s_uuid, heroView,targets); this.createSkill(s_uuid, heroView,targets);
}, delay); },delay);
} }
return true; return true;
} }
@@ -190,7 +214,7 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
/** /**
* 创建技能实体 * 创建技能实体
*/ */
private createSkill(s_uuid: number, caster: HeroViewComp,targets:Vec3[]=[],ext_dmg:number=0) { private createSkill(s_uuid: number, caster: HeroViewComp,targets:Vec3[]=[],ext_dmg:number=0,splash:number=0) {
// 检查节点有效性 // 检查节点有效性
if (!caster.node || !caster.node.isValid) { if (!caster.node || !caster.node.isValid) {
console.warn("[SACastSystem] 施法者节点无效"); console.warn("[SACastSystem] 施法者节点无效");
@@ -215,7 +239,7 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
const targetPos = targets[0]; // 使用第一个目标位置 const targetPos = targets[0]; // 使用第一个目标位置
// console.log(`[SACastSystem]: ${s_uuid}, 起始位置: ${startPos}, 目标位置: ${targetPos}`); // console.log(`[SACastSystem]: ${s_uuid}, 起始位置: ${startPos}, 目标位置: ${targetPos}`);
// 加载技能实体(包括预制体、组件初始化等) // 加载技能实体(包括预制体、组件初始化等)
skill.load(startPos, parent, s_uuid, targetPos, caster,ext_dmg); skill.load(startPos, parent, s_uuid, targetPos, caster,ext_dmg,splash);
} }
/** /**

View File

@@ -202,6 +202,21 @@ export class TalComp extends ecs.Comp {
const talent = this.Tals[uuid]; const talent = this.Tals[uuid];
const heroAttrs=this.ent.get(HeroAttrsComp); const heroAttrs=this.ent.get(HeroAttrsComp);
switch(talent.effet){ switch(talent.effet){
case TalEffet.ATK_DMG:
heroAttrs.addCountTal(TalEffet.ATK_DMG, talent.value + talent.value_add);
break;
case TalEffet.SKILL_DMG:
heroAttrs.addCountTal(TalEffet.SKILL_DMG, talent.value + talent.value_add);
break;
case TalEffet.LDMG:
heroAttrs.addCountTal(TalEffet.LDMG, talent.value + talent.value_add);
break;
// case TalEffet.HP:
// heroAttrs.addCountTal(TalEffet.HP, talent.value + talent.value_add);
// break;
// case TalEffet.MP:
// heroAttrs.addCountTal(TalEffet.MP, talent.value + talent.value_add);
// break;
case TalEffet.WFUNY: case TalEffet.WFUNY:
heroAttrs.addCountTal(TalEffet.WFUNY, talent.value + talent.value_add); heroAttrs.addCountTal(TalEffet.WFUNY, talent.value + talent.value_add);
break; break;

View File

@@ -12,6 +12,9 @@ export class SDataCom extends ecs.Comp {
group:BoxSet=BoxSet.HERO group:BoxSet=BoxSet.HERO
fac: number = 0; // 0:hero 1:monster fac: number = 0; // 0:hero 1:monster
s_uuid:number=0 s_uuid:number=0
ext_dmg:number=0 //额外伤害
splash:number=0 //溅射伤害
dmg_ratio:number=1 //伤害比例
hit_count:number=0 //击中数量 hit_count:number=0 //击中数量
reset() { reset() {
this.Attrs=null this.Attrs=null
@@ -20,6 +23,9 @@ export class SDataCom extends ecs.Comp {
this.s_uuid=0 this.s_uuid=0
this.caster=null this.caster=null
this.hit_count=0 this.hit_count=0
this.ext_dmg=0
this.splash=0
this.dmg_ratio=1
} }
} }

View File

@@ -29,7 +29,7 @@ export class Skill extends ecs.Entity {
this.addComponents<SMoveDataComp>(SMoveDataComp); this.addComponents<SMoveDataComp>(SMoveDataComp);
} }
load(startPos: Vec3, parent: Node, s_uuid: number, targetPos: Vec3, load(startPos: Vec3, parent: Node, s_uuid: number, targetPos: Vec3,
caster:HeroViewComp,ext_dmg:number=0) { caster:HeroViewComp,ext_dmg:number=0,splash:number=0) {
const config = SkillSet[s_uuid]; const config = SkillSet[s_uuid];
if (!config) { if (!config) {
@@ -93,6 +93,8 @@ export class Skill extends ecs.Entity {
sDataCom.Attrs={...cAttrsComp.Attrs} sDataCom.Attrs={...cAttrsComp.Attrs}
sDataCom.s_uuid=s_uuid sDataCom.s_uuid=s_uuid
sDataCom.fac=cAttrsComp.fac sDataCom.fac=cAttrsComp.fac
sDataCom.ext_dmg=ext_dmg
sDataCom.splash=splash
} }
/** 模块资源释放 */ /** 模块资源释放 */