Files
heros/assets/script/game/skills/SkillCom.ts
2025-07-21 08:07:53 +08:00

333 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { _decorator,Collider2D ,Contact2DType,v3,IPhysics2DContact,Vec3, tween, math, RigidBody2D, Animation, sp, Tween} from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { AType, DTType, EType, RType, SkillSet, SType, TGroup } from "../common/config/SkillSet";
import { BoxSet, FacSet } from "../common/config/BoxSet";
import { HeroViewComp } from "../hero/HeroViewComp";
import { BezierMove } from "../BezierMove/BezierMove";
import { FightConComp } from "../map/FightConComp";
import { MonModelComp } from "../hero/MonModelComp";
import { FightSet } from "../common/config/Mission";
import { HeroModelComp } from "../hero/HeroModelComp";
const { ccclass, property } = _decorator;
/** 视图层对象 */
@ccclass('SkillCom')
@ecs.register('SkillCom')
export class SkillCom extends CCComp {
s_uuid:number = 0;
s_name:string = "";
hero:number = 0;
speed:number = 200;
scale:number = 1;
angle:number = 0;
atk_count:number = 0;
is_destroy:boolean = false;
enemys:any = [];
AType: number = 0; // 运动类型
startPos: Vec3 = v3(); // 起始位置
targetPos: Vec3 = v3(); // 目标位置
duration: number = 0; // 技能持续时间
prefabName: string = ""; // 预制体名称
animName: string = "";
group:number = 0; //阵营
fac:number=0; //阵营
caster:any=null;
distance_x:number=0;
distance_y:number=0;
ap:number=0;
heal:number=0;
shield:number=0;
burn_count:number=0;
burn_value:number=0;
stun_time:number=0;
stun_ratto:number=0;
frost_ratto:number=0;
frost_time:number=0;
FIGHTCON:FightConComp=null;
run_time:number=0;
hited_time:number=0;
hit_count:number=0;
spine:sp.Skeleton=null;
anim:Animation=null;
tweenInstance:Tween<any> = null;
t_end_x:number=0;
caster_crit:number=0;
caster_crit_d:number=0;
puncture:number=0;
puncture_damage:number=0;
EType:any=null
private moveDirection: Vec3 | null = null; // 添加一个属性来存储移动方向
protected onLoad(): void {
this.FIGHTCON=this.node.parent.getComponent(FightConComp)
}
start() {
this.EType=SkillSet[this.s_uuid].EType
this.node.setPosition(this.startPos.x,this.startPos.y,0)
if(this.node.getChildByName('anm')){
this.spine=this.node.getChildByName('anm').getComponent('sp.Skeleton') as sp.Skeleton;
}else{
this.anim=this.node.getComponent(Animation)
}
oops.message.on(GameEvent.MissionEnd, this.doDestroy, this);
this.node.active = true;
// //console.log("[SkillCom]:caster",this.caster)
let collider = this.getComponent(Collider2D);
if(collider) {
collider.group = this.group;
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
}
let bm=this.node.getComponent(BezierMove)
// //console.log(this.group +"技能 collider ",collider);
switch(SkillSet[this.s_uuid].AType){
case AType.parabolic:
this.node.angle +=10
// bm.speed=700
if(this.group==BoxSet.MONSTER) {bm.controlPointSide=-1 }
bm.rotationSmoothness=0.6
bm.moveTo(this.targetPos)
break;
case AType.linear:
let s_x=this.startPos.x
let s_y=this.startPos.y
let t_x=this.targetPos.x
let t_y=this.targetPos.y
// 设定目标x
this.targetPos.x = 400;
if(this.group == BoxSet.MONSTER) {
bm.controlPointSide = -1;
this.targetPos.x = -400;
}
// 计算斜率
const k = (t_y - s_y) / (t_x - s_x);
// 按直线公式计算新的y
this.targetPos.y = k * (this.targetPos.x - s_x) + s_y;
bm.controlPointOffset=0
bm.rotationSmoothness=0.6
bm.moveTo(this.targetPos);
break;
case AType.StartEnd:
// 2段位移先升高然后移到目的地
this.executeTwoStageMovement();
break;
case AType.fixedEnd:
this.node.setPosition(this.targetPos.x,this.targetPos.y,0)
if(this.node.getComponent(Animation)){
let anim = this.node.getComponent(Animation);
// //console.log("[SkillCom]:has anim",anim)
anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this);
}
if(this.node.getChildByName('anm')){
if(this.node.getChildByName('anm').getComponent('sp.Skeleton')){
// //console.log("[SkillCom]:has spine",this.spine)
this.spine.setCompleteListener((trackEntry) => {
this.onAnimationFinished()
// //console.log("[SkillCom]:[track %s][animation %s] complete: %s", trackEntry.trackIndex);
});
}
}
break;
case AType.fixedStart: //都是buff
this.node.setPosition(this.startPos.x,this.startPos.y,0)
this.scheduleOnce(()=>{
this.do_buff()
//console.log("[SkillCom]:fixedStart",SkillSet[this.s_uuid].in-0.2)
},SkillSet[this.s_uuid].in-0.2)
break;
}
}
onAnimationFinished(){
if(SkillSet[this.s_uuid].EType==EType.timeEnd) return
this.is_destroy=true
}
//范围伤害
range_damage(){
console.log("[SkillCom]:range_damage",this.s_uuid)
let enemys=ecs.query(ecs.allOf(MonModelComp))
if(this.fac==FacSet.MON) enemys=ecs.query(ecs.allOf(HeroModelComp))
enemys.forEach(entity => {
let view=entity.get(HeroViewComp)
if(view){
let dis_x =Math.abs(this.node.position.x-view.node.position.x)
let dis_y =Math.abs(this.node.position.y-view.node.position.y)
if(dis_x > SkillSet[this.s_uuid].with||dis_y > SkillSet[this.s_uuid].with) return
this.single_damage(view,true)
}
});
}
//单体伤害
single_damage(target:HeroViewComp,is_range:boolean=false){
// //console.log("[SkillCom]:onBeginContact hit_count:",this.hit_count,SkillSet[this.s_uuid].hit)
// if(this.hit_count > 0&&!is_range) this.ap=this.ap*(50+this.puncture_damage)/100 // 穿刺后 伤害减半,过滤范围伤害
if(target == null) return;
target.do_atked(this.ap,this.caster_crit,this.caster_crit_d,
this.burn_count,this.burn_value,
this.stun_time,this.stun_ratto,
this.frost_time,this.frost_ratto) // ap 及暴击 属性已经在skill.ts 处理
console.log("[SkillCom]:single_damage t:tp:rtp",this.node.position,this.targetPos,target.node.position)
if(SkillSet[this.s_uuid].debuff>0){
let deUP =this.get_debuff() // 因为不是每个技能都需要,debuff的增益在这里处理, ap 及暴击 属性已经在skill.ts 处理
let debuff=SkillSet[this.s_uuid]
let dev=debuff.deV*(100+deUP.deV)/100
let deR=debuff.deR+deUP.deR
dev=Math.round(dev*100)/100
let deC=debuff.deC+deUP.deC //dec只作为次数叠加
// //console.log("[SkillCom]:debuff",SkillSet[this.s_uuid].name,debuff.debuff,deUP.deV,deUP.deC)
target.add_debuff(debuff.debuff,dev,deC,deR)
}
this.hit_count++
if(this.hit_count>=(SkillSet[this.s_uuid].hit+this.puncture)&&(SkillSet[this.s_uuid].DTType!=DTType.range)) this.is_destroy=true // 技能命中次数
}
onBeginContact (seCol: Collider2D, oCol: Collider2D) {
// //console.log(this.scale+"碰撞开始 ",seCol,oCol);
let target = oCol.getComponent(HeroViewComp)
if(oCol.group!=this.group){
if(target == null) return;
console.log("[SkillCom]:onBeginContact oCol||seCol",oCol.node.position,seCol.node.position)
this.single_damage(target)
// this.ent.destroy()
}
}
get_debuff(){ //debuff 加成
let debuff = {deV:0,deC:0,deR:0}
let buff=null
if(this.caster.is_master) buff = this.FIGHTCON.hero_buff
if(this.caster.is_boss||this.caster.is_kalami) buff = this.FIGHTCON.enemy_buff
if(buff==null) return debuff
debuff.deV=buff.DEBUFF_VALUE
debuff.deC=buff.DEBUFF_COUNT
debuff.deR=buff.DEBUFF_UP
return debuff
}
/**
* 执行2段位移先升高然后移到目的地
*/
private executeTwoStageMovement() {
const totalDuration = SkillSet[this.s_uuid].in-0.1
const firstStageDuration = totalDuration * 0.4; // 第一段占40%时间
const secondStageDuration = totalDuration * 0.6; // 第二段占60%时间
// 第一段:升高
const riseHeight = 100; // 升高高度
const midPosition = v3(this.node.position.x, this.node.position.y + riseHeight, 0);
// 第二段:移动到目标位置
const finalPosition = v3(this.targetPos.x, this.targetPos.y, 0);
// 创建缓动序列
tween(this.node)
// 第一段:升高
.to(firstStageDuration, { position: midPosition }, {
easing: 'quadOut' // 使用二次缓出效果,让上升更自然
})
// 第二段:移动到目标位置
.to(secondStageDuration, { position: finalPosition }, {
easing: 'quadInOut' // 使用二次缓入缓出效果
}).call(()=>{
this.do_buff()
})
.start();
}
private do_buff(){
//console.log("[SkillCom]:do_buff")
let teams=ecs.query(ecs.allOf(HeroModelComp))
if(this.fac==FacSet.MON) teams=ecs.query(ecs.allOf(MonModelComp))
if(SkillSet[this.s_uuid].TGroup==TGroup.Team||SkillSet[this.s_uuid].TGroup==TGroup.Self) {
teams.forEach(entity => {
let view=entity.get(HeroViewComp)
if(view.node.position.x==this.targetPos.x){
if(SkillSet[this.s_uuid].SType==SType.heal){
view.add_hp(this.heal,false)
}
if(SkillSet[this.s_uuid].SType==SType.shield){
view.add_shield(this.shield)
}
}
});
}
}
update(deltaTime: number) {
let config=SkillSet[this.s_uuid]
if(smc.mission.pause) {
if(this.spine) this.spine.paused=true
if(this.anim) this.anim.pause()
return;
}
if(this.anim) this.anim.resume()
if(this.spine) this.spine.paused=false
if (!this.node || !this.node.isValid) return;
if(config.EType==EType.timeEnd){
this.run_time+=deltaTime
if(this.run_time>config.in){
// //console.log("[SkillCom]: timeEnd destroy",this.s_uuid,this.run_time)
this.is_destroy=true
}
}
//范围伤害
this.hited_time+=deltaTime
if(this.hited_time>config.hited&&(SkillSet[this.s_uuid].DTType==DTType.range)){
this.hited_time=0
this.range_damage()
}
//直线移动
// if(this.AType == AType.linear) this.startLinearMove(deltaTime);
this.toDestroy();
}
toDestroy() {
if(this.is_destroy){
if (this.ent) {
this.ent.destroy();
} else {
// 如果ent不存在直接销毁节点
if (this.node && this.node.isValid) {
this.node.destroy();
}
}
}
}
doDestroy(){
// //console.log("[SkillCom]:doDestroy")
this.is_destroy=true
}
to_console(value:any,value2:any=null,value3:any=null){
//console.log("[SkillCom]:["+this.s_name+this.s_uuid+"]:",value,value2,value3)
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.is_destroy = false;
this.AType = 0;
this.speed = 0;
this.startPos.set();
this.targetPos.set();
this.moveDirection = null; // 重置移动方向
// 先移除所有碰撞回调
const collider = this.getComponent(Collider2D);
if (collider) {
collider.off(Contact2DType.BEGIN_CONTACT);
}
this.scheduleOnce(() => {
this.node.destroy();
}, 0);
}
}