@@ -4,6 +4,7 @@ import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp" ;
import { DTType , RType , SkillConfig , SkillKind , SkillSet , TGroup , SkillOverrides , mergeSkillParams } from "../common/config/SkillSet" ;
import { Skill } from "../skill/Skill" ;
import { SkillView } from "../skill/SkillView" ;
import { smc } from "../common/SingletonModuleComp" ;
import { HeroDisVal , HeroInfo , HType } from "../common/config/heroSet" ;
import { Attrs } from "../common/config/HeroAttrs" ;
@@ -90,9 +91,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
targetEids = this . collectFriendlyTargetEids ( FacSet . HERO , undefined , true ) ; // 获取所有英雄阵营的目标
}
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math . min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
const castTimes = 1 + cNum ;
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1) 默认不再参与风怒多发
const summonCount = this . resolveSummonCount ( config ) ;
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = summonCount > 1 ? 0 : Math.min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
const castTimes = Math . max ( summonCount , 1 + cNum ) ;
// 构造一个模拟的 HeroAttrsComp 用于数值计算,只包含基础卡牌伤害计算所需的属性
const mockAttrs = new HeroAttrsComp ( ) ;
@@ -145,14 +148,23 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
mLogger . log ( this . debugMode , "SCastSystem" , "forceCastCardSkill: casting skill" , s_uuid , "castTimes" , castTimes , "targetPos" , targetPos , "isFriendly" , isFriendly , "buff_value" , config . buff_value , "timed_buff_id" , config . timed_buff_id ) ;
for ( let i = 0 ; i < castTimes ; i ++ ) {
if ( isFriendly ) {
const friendlyTargets = this . resolveFriendlyTargets ( targetEids , FacSet . HERO ) ;
mLogger . log ( this . debugMode , "SCastSystem" , "friendlyTargets count =" , friendlyTargets . length ) ;
if ( f riendlyTargets . length === 0 ) continue ;
this . applyFriendlySkillEffects ( s_uuid , cardLv , config , null as any , mockAttrs , friendlyTarg ets , spawnPos ) ;
// 多发/召唤按间隔错峰执行: i=0 立即,其余延迟 i*SUMMON_INTERVAL( 卡牌无施法主体, 用 setTimeout 并在回调内做效期校验)
const exec = ( ) = > {
if ( ! smc . mission . play || smc . mission . pause ) return ;
if ( isF riendly) {
const friendlyTargets = this . resolveFriendlyTargets ( targetEids , FacS et . HERO ) ;
mLogger . log ( this . debugMode , "SCastSystem" , "friendlyTargets count =" , friendlyTargets . length ) ;
if ( friendlyTargets . length === 0 ) return ;
this . applyFriendlySkillEffects ( s_uuid , cardLv , config , null as any , mockAttrs , friendlyTargets , spawnPos ) ;
} else {
const enemyTargetPos = this . resolveRepeatCastTargetPos ( targetPos , i ) ;
this . createSkillEntityForCard ( s_uuid , cardLv , mockAttrs , spawnPos , enemyTargetPos , i , overrides ) ;
}
} ;
if ( i === 0 ) {
exec ( ) ;
} else {
const enemyTargetPos = this . resolveRepeatCastTargetPos ( targetPos , i ) ;
this . createSkillEntityForCard ( s_uuid , cardLv , mockAttrs , spawnPos , enemyTargetPos , i , overrides ) ;
setTimeout ( exec , i * FightSet . SUMMON_INTERVAL * 1000 ) ;
}
}
}
@@ -184,6 +196,29 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
/** 查询缓存:避免每帧重复创建 matcher */
private heroMatcher : ecs.IMatcher | null = null ;
/** 技能 prefab 召唤数量缓存: sp_name -> summon_count */
private summonCountCache : Map < string , number > = new Map ( ) ;
/**
* 解析技能 prefab 上 SkillView.summon_count 配置的单次施法召唤数量。
* 仅使用已缓存的 prefab( oops.res.get 同步命中),未加载时回退 1( 单体) , 不引入异步等待。
*/
private resolveSummonCount ( config : SkillConfig ) : number {
const cached = this . summonCountCache . get ( config . sp_name ) ;
if ( cached !== undefined ) return cached ;
const path = ` game/skill/atk/ ${ config . sp_name } ` ;
let count = 1 ;
const prefab = oops . res . get ( path , Prefab ) as Prefab | null ;
if ( prefab ) {
const view = prefab . data ? . getComponent ( SkillView ) ;
if ( view ) {
count = Math . max ( 1 , Math . floor ( view . summon_count ) ) ;
}
this . summonCountCache . set ( config . sp_name , count ) ;
}
return count ;
}
/** 获取英雄查询条件(包含属性与视图组件) */
private getHeroMatcher ( ) : ecs . IMatcher {
if ( ! this . heroMatcher ) {
@@ -273,9 +308,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math . min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
const castTimes = 1 + cNum ;
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1) 默认不再参与风怒多发
const summonCount = this . resolveSummonCount ( config ) ;
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = summonCount > 1 ? 0 : Math.min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
const castTimes = Math . max ( summonCount , 1 + cNum ) ;
let val = ""
if ( castTimes > 1 ) {
val = "*" + castTimes . toString
@@ -283,14 +320,19 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView . skill_name ( val , s_uuid , triggerType )
for ( let i = 0 ; i < castTimes ; i ++ ) {
if ( ! heroView . node || ! heroView . node . isValid ) return ;
if ( isFriendly ) {
const friendlyTargets = this . resolveFriendlyTargets ( targetEids , heroAttrs . fac ) ;
if ( friendlyTargets . length === 0 ) continue ;
this . applyFriendlySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , friendlyTargets , null ) ;
} else {
// 多发/召唤按间隔错峰执行: i=0 立即,其余延迟 i*SUMMON_INTERVAL
heroView . scheduleOnce ( ( ) = > {
if ( ! smc . mission . play || smc . mission . pause ) return ;
if ( ! heroView . node || ! heroView . node . isValid || heroAttrs . is_dead ) return ;
if ( isFriendly ) {
const friendlyTargets = this . resolveFriendlyTargets ( targetEids , heroAttrs . fac ) ;
if ( friendlyTargets . length === 0 ) return ;
this . applyFriendlySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , friendlyTargets , null ) ;
return ;
}
const enemyTargetPos = this . resolveRepeatCastTargetPos ( targetPos , i ) ;
this . applyEnemySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , enemyTargetPos , i , overrides ) ;
}
} , i * FightSet . SUMMON_INTERVAL ) ;
}
}
@@ -343,12 +385,16 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const skillLv = castPlan . skillLv ;
const overrides = castPlan . overrides ;
let config = SkillSet [ s_uuid ] ;
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math . min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
if ( ! config ) return ;
config = mergeSkillParams ( config , overrides ) ;
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1) 默认不再触发风怒
const summonCount = this . resolveSummonCount ( config ) ;
const isSummonSkill = summonCount > 1 ;
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = isSummonSkill ? 0 : Math.min ( 2 , Math . max ( 0 , Math . floor ( overrides ? . num ? ? 0 ) ) ) ;
//播放前摇技能动画
heroView . playReady ( config . readyAnm ) ;
//播放角色攻击动画
@@ -356,8 +402,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 因为 castSkill 只由 update 循环调用,处理的必然是 heroAttrs.skills 中的普通技能
// 特殊触发技能(call/dead/atking等)走的是 forceCastTriggerSkill, 不会调用 castSkill
// 召唤类技能( summon_count>1) 不再计入普攻链, 避免触发 atking( 风怒类) 附加技能
const skillIds = heroAttrs . getSkillIds ( ) ;
if ( skillIds . includes ( s_uuid ) ) {
if ( ! isSummonSkill && skillIds . includes ( s_uuid ) ) {
heroAttrs . atk_count ++ ;
this . checkAndTriggerAtkingSkills ( heroAttrs , heroView ) ;
}
@@ -370,18 +417,21 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView . scheduleOnce ( ( ) = > {
if ( ! smc . mission . play || smc . mission . pause || ! smc . mission . in_fight ) return ;
if ( ! heroView . node || ! heroView . node . isValid || heroAttrs . is_dead ) return ;
const castTimes = 1 + cNum ;
const castTimes = Math . max ( summonCount , 1 + cNum ) ;
for ( let i = 0 ; i < castTimes ; i ++ ) {
if ( ! smc . mission . play || smc . mission . pause || ! smc . mission . in_fight ) return ;
if ( ! heroView . node || ! heroView . node . isValid || heroAttrs . is_dead ) return ;
if ( castPlan . isFriendly ) {
const friendlyTargets = this . resolveFriendlyTargets ( castPlan . targetEids , heroAttrs . fac ) ;
if ( friendlyTargets . length === 0 ) return ;
this . apply FriendlySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , friendlyTargets , null ) ;
continue ;
}
const enemyTargetPos = this . resolveRepeatCastTargetPos ( castPlan . targetPos , i ) ;
this . applyEnemySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , enemyTargetPos , i , overrides ) ;
// 多发/召唤按间隔错峰执行: i=0 立即,其余延迟 i*SUMMON_INTERVAL
heroView . scheduleOnce ( ( ) = > {
if ( ! smc . mission . play || smc . m ission . pause || ! smc . mission . in_fight ) return ;
if ( ! heroView . node || ! heroView . node . isValid || heroAttrs . is_dead ) return ;
if ( castPlan . isFriendly ) {
const friendlyTargets = this . resolve FriendlyTargets ( castPlan . targetEids , heroAttrs . fac ) ;
if ( friendlyTargets . length === 0 ) return ;
this . applyFriendlySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , friendlyTargets , null ) ;
return ;
}
const enemyTargetPos = this . resolveRepeatCastTargetPos ( castPlan . targetPos , i ) ;
this . applyEnemySkillEffects ( s_uuid , skillLv , config , heroView , heroAttrs , enemyTargetPos , i , overrides ) ;
} , i * FightSet . SUMMON_INTERVAL ) ;
}
} , delay ) ;
heroAttrs . triggerSkillCD ( s_uuid ) ;
@@ -392,11 +442,14 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
SkillTriggerHelper . trigger ( SkillTriggerType . Atking , heroAttrs , heroView ) ;
}
/** 多发/召唤的纵向扇形偏移档位(像素),索引按 中/上/下/上上/下下... 循环 */
private static readonly REPEAT_Y_OFFSETS : number [ ] = [ 0 , 15 , - 15 , 30 , - 30 , 45 , - 45 , 60 , - 60 ] ;
private resolveRepeatCastTargetPos ( targetPos : Vec3 | null , castIndex : number ) : Vec3 | null {
if ( ! targetPos ) return null ;
if ( castIndex === 1 ) return new Vec3 ( targetPos . x , targetPos . y + 15 , targetPos . z ) ;
if ( castIndex === 2 ) return new Vec3 ( targetPos . x , targetPos . y - 15 , targetPos . z ) ;
return targetPos ;
const offset = SCastSystem . REPEAT_Y_OFFSETS [ castIndex % SCastSystem . REPEAT_Y_OFFSETS . length ] ;
if ( offset === 0 ) return targetPos ;
return new Vec3 ( targetPos . x , targetPos . y + offset , targetPos . z ) ;
}
/** 构建技能尝试顺序:优先主动技能,其次普攻 */
@@ -431,9 +484,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
private resolveRepeatCastStartPos ( startPos : Readonly < Vec3 > , castIndex : number ) : Vec3 {
if ( castIndex === 1 ) return new Vec3 ( startPos . x , startPos . y + 15 , startPos . z ) ;
if ( castIndex === 2 ) return new Vec3 ( startPos . x , startPos . y - 15 , startPos . z ) ;
return startPos . clone ( ) ;
const offset = SCastSystem . REPEAT_Y_OFFSETS [ castIndex % SCastSystem . REPEAT_Y_OFFSETS . length ] ;
if ( offset === 0 ) return startPos . clone ( ) ;
return new Vec3 ( startPos . x , startPos . y + offset , startPos . z ) ;
}
/**