Files
heros/assets/script/game/hero/HeroViewComp.ts

713 lines
24 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 { Vec3, _decorator , v3,Collider2D,Contact2DType,Label ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Color} 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 { HeroSpine } from "./HeroSpine";
import { BoxSet, FacSet } from "../common/config/BoxSet";
import { smc } from "../common/SingletonModuleComp";
import { Timer } from "../../../../extensions/oops-plugin-framework/assets/core/common/timer/Timer";
import { BuffAttr, DebuffAttr, SkillSet, TGroup, TType } from "../common/config/SkillSet";
import { BuffComp } from "./BuffComp";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { FightConComp } from "../map/FightConComp";
import { EquipSpecialAttr } from "../common/config/Equips";
import { FightSet, TooltipTypes } from "../common/config/Mission";
import { getApIncrease, getHpIncrease, getUpExp, HeroPos } from "../common/config/heroSet";
import { FriendModelComp } from "./FriendModel";
import { MasterModelComp } from "./MasterModel";
import { RandomManager } from "db://oops-framework/core/common/random/RandomManager";
const { ccclass, property } = _decorator;
/** 角色显示组件 */
@ccclass('HeroViewComp') // 定义为 Cocos Creator 组件
@ecs.register('HeroView', false) // 定义为 ECS 组件
export class HeroViewComp extends CCComp {
FIGHTCON:FightConComp=null!
BUFFCOMP:BuffComp=null!
as: HeroSpine = null!
status:String = "idle"
hero_uuid:number = 1001;
hero_name : string = "hero";
fight_pos:number=0;
lv:number =1;
exp:number = 0;
next_exp:number = 100;
scale: number = 1; /** 角色阵营 1hero -1 :mon */
type: number = 0; /**角色类型 0近战-需要贴身 1远程-保持距离 2辅助 */
fac:number=0; //阵营 0hero 1monster
box_group:number = BoxSet.HERO;
is_dead:boolean = false; //是否摧毁
is_stop:boolean = false;
is_atking:boolean = false;
is_boss:boolean = false;
is_big_boss:boolean = false;
is_master:boolean =false;
is_friend:boolean =false;
is_kalami:boolean =false;
hp: number = 100; /** 血量 */
hp_max: number = 100; /** 最大血量 */
buff_hp:number=0;
pwt:Timer = new Timer(1); //计时器
ap: number = 10; /**攻击力 */
buff_ap:number=0;
// atk_speed: number = 1;
cd: number = 1.3; /**攻击速度 攻击间隔 */
dis: number = 80;
at: number = 0; /** 冷却时间 */
atk_skill:number=0;
puncture:number=0; //穿刺敌人伤害后方敌人个数
puncture_damage:number=0; //后伤害加成
def: number = 0; //防御
vun: number = 0; //易伤
burn_count:number=0; //易伤次数
burn_value:number=0; //易伤值
stun_time:number=0; //眩晕加成
stun_ratto:number=0; //攻击眩晕概率加成
dod: number = 0; //闪避率
dod_no:boolean=false;
crit:number=0; //暴击率
crit_no:boolean=false; //暴击免疫
crit_d:number=0; //暴击伤害
wfuny:number=0; //风怒概率
frost_ratto:number=0; //冰冻概率
frost_time:number=0; //冰冻时间
shield:number = 0; //护盾,免伤1次减1
speed: number = 100; /** 角色移动速度 */
ospeed: number = 100; /** 角色初始速度 */
atk_count: number = 0;
atked_count: number = 0;
atk_add_count:number=0;
stop_cd: number = 0; /*停止倒计时*/
speek_time:number = 0;
is_stop_temp:boolean = false
Friend_alive_cd:Timer=new Timer(10)
double_dead:boolean=false
double_atked:boolean=false
BUFF_DEFS: Array<{value: number, count: number}> = [] //防御提升
BUFF_ATKS: Array<{value: number, count: number}> = [] //攻击提升
BUFF_CDS: Array<{value: number, count: number}> = [] //攻击加速
BUFF_DEDOWN:Array<{value: number, count: number}> = [] //debuff 概率降低
DEBUFF_BURNS: Array<{value: number, count: number}> = [] //易伤
DEBUFF_DEATKS: Array<{value: number, count: number}> = [] //减攻击
DEBUFF_DECDS: Array<{value: number, count: number}> = [] //减攻击速度
DEBUFF_SLOW: number = 0; //减速
DEBUFF_FROST: number = 0; //冰冻
DEBUFF_STUN: number = 0; //眩晕
private damageQueue: Array<{
damage: number,
isCrit: boolean,
delay: number
}> = [];
private isProcessingDamage: boolean = false;
private damageInterval: number = 0.2; // 伤害数字显示间隔
private timer:Timer=new Timer(1);
onLoad() {
this.as = this.getComponent(HeroSpine);
this.FIGHTCON=this.node.parent.getComponent(FightConComp);
console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON)
this.on(GameEvent.ChangeATK_EQUIP_SPECIAL_ATTR,this.change_atk,this)
this.on(GameEvent.UpdateHP,this.update_hp,this)
this.on(GameEvent.EXPUP,this.exp_up,this)
// let anm = this.node.getChildByName("anm")
// anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8);
}
/** 视图层逻辑代码分离演示 */
start () {
this.as.idle()
this.BUFFCOMP=this.node.getComponent(BuffComp);
/** 方向 */
this.node.setScale(this.scale,1);
this.node.getChildByName("top").setScale(this.scale,1);
if(this.is_boss){
this.node.setScale(this.node.scale.x*1.5,this.node.scale.y*1.5);
this.node.getChildByName("top").getChildByName("sboss").active = true;
}
/* 显示角色血量 */
this.node.getChildByName("top").getChildByName("hp").active = true;
if(this.is_friend){ //只有伙伴需要召唤后添加hp 怪物,和boss 不要设置减hp debuff 主要 一开始就战斗开始就存在,所以不需要
this.hp+=this.FIGHTCON.friend_buff.HP*this.hp_max/100
}
}
update(dt: number){
if(!smc.mission.play||smc.mission.pause) return
// if(this.is_dead) {
// this.ent.destroy();
// return
// }
if(this.is_dead) {
// console.log("[HeroViewComp]:alive friend cd",this.Friend_alive_cd ,this.Friend_alive_cd.elapsedTime)
if(this.Friend_alive_cd.update(dt)){
this.to_alive()
}
}
if(this.timer.update(dt)){
// this.add_ap(10)
};
if(this.DEBUFF_FROST > 0){
this.DEBUFF_FROST -=dt;
}
if(this.DEBUFF_STUN > 0){
this.DEBUFF_STUN -=dt;
}
this.in_stop(dt);
// 处理伤害队列
this.processDamageQueue();
}
change_atk(e:GameEvent,data:any){
if(!this.is_master) return
}
get isActive() {
return this.ent.has(HeroViewComp) && this.node?.isValid;
}
hide_info(){
this.node.getChildByName("top").getChildByName("ihp").active = false;
this.node.getChildByName("top").getChildByName("iap").active = false;
}
//状态切换
status_change(type:string){
this.status=type
if(type == "idle"){
this.as.idle()
// this.as.change_default("idle")
}
if(type == "move"){
this.as.move()
// this.as.change_default("move")
}
}
add_shield(shield:number){
this.shield =shield
if(this.shield>6) this.shield=6
if(this.shield>0) this.BUFFCOMP.show_shield(true)
}
// add_cd(cd: number){
// this.cd += this.cd*((100-cd)/100);
// this.BUFFCOMP.buff_get("cd")
// }
/**
* 增加英雄的攻击AP
* @param ap 要增加的攻击。
*/
add_ap(ap: number,is_num:boolean=true){
console.log("[HeroViewComp]:add_ap add:",ap,this.ap)
if(is_num){
this.ap += Math.floor(ap);
}else{
this.ap += Math.floor(ap/100*this.ap);
}
this.BUFFCOMP.vmdata_update()
this.BUFFCOMP.tooltip(TooltipTypes.apup,ap.toFixed(0));
oops.message.dispatchEvent(GameEvent.APChange,{is_master:this.is_master,fac:this.fac})
}
de_ap(ap: number,is_num:boolean=true){
console.log("[HeroViewComp]:de_ap de:",ap,this.ap)
if(is_num){
this.ap -= Math.floor(ap);
}else{
this.ap -= Math.floor(ap/100*this.ap);
}
this.BUFFCOMP.vmdata_update()
}
update_hp(e:GameEvent,data:any){
console.log("[HeroViewComp]:update_hp",data)
if(this.is_master===data.is_master&&this.fac===FacSet.HERO){
this.buff_hp += data.hp
if(data.hp > 0){
this.hp += this.hp_max*data.hp/100
if(this.hp > this.hp_max*(100+this.buff_hp)/100){
this.hp=this.hp_max*(100+this.buff_hp)/100
}
}
}
}
add_hp_max(hp: number=0,is_num:boolean=true){
this.hp_max += Math.floor(hp) ;
this.hp += Math.floor(hp*(100+this.buff_hp)/100) ;
this.BUFFCOMP.vmdata_update(true)
this.BUFFCOMP.tooltip(TooltipTypes.hpup,hp.toFixed(0));
}
de_hp_max(hp: number=0,is_num:boolean=true){ //最大值 只存在数值添加, 比例通过buff_hp处理
console.log("[HeroViewComp]:de_hp_max de:",hp,this.hp_max)
this.hp_max -= Math.floor(hp) ;
this.BUFFCOMP.vmdata_update(true)
}
add_hp(hp: number = 0,is_num:boolean=true) {
this.BUFFCOMP.heathed();
let real_hp=0
let hp_max=Math.floor(this.hp_max*(100+this.buff_hp)/100)
let lost_hp=hp_max-this.hp
if(is_num){
if(lost_hp > hp){
real_hp=Math.floor(hp);
}else{
real_hp=lost_hp;
}
}else{
if(lost_hp > hp/100*hp_max){
real_hp=Math.floor(hp/100*hp_max);
}else{
real_hp=lost_hp;
}
}
if(real_hp > 0){
this.hp+=real_hp;
this.BUFFCOMP.tooltip(TooltipTypes.health,real_hp.toFixed(0));
}
this.BUFFCOMP.vmdata_update(true)
}
/** 静止时间 */
in_stop (dt: number) {
if(this.stop_cd > 0){
this.stop_cd -= dt;
if(this.stop_cd <= 0){
this.stop_cd = 0;
this.is_atking = false;
}
}
}
master_count_atk_count(){ //主将攻击次数 主要有装备加成,需要单独处理
if(!this.is_master) return
this.atk_count+=1
if(this.atk_count< FightSet.ATK_ADD_FRIEND_COUNT) return
if(this.FIGHTCON.atk_add_glod >0) smc.vmdata.mission_data.gold+=this.FIGHTCON.atk_add_glod
let friends=ecs.query(ecs.allOf(FriendModelComp))
friends.forEach(friend=>{
let friend_view=friend.get(HeroViewComp)
if(this.FIGHTCON.atk_add_friend_atk>0) {
friend_view.add_ap(this.FIGHTCON.atk_add_friend_atk)
}
if(this.FIGHTCON.atk_add_friend_hp>0) {
friend_view.add_hp_max(this.FIGHTCON.atk_add_friend_hp)
}
})
let master=ecs.query(ecs.allOf(MasterModelComp))
master.forEach(master=>{
let master_view=master.get(HeroViewComp)
if(this.FIGHTCON.atk_add_master_atk>0) {
master_view.add_ap(this.FIGHTCON.atk_add_master_atk)
}
if(this.FIGHTCON.atk_add_master_hp>0) {
master_view.add_hp_max(this.FIGHTCON.atk_add_master_hp)
}
})
this.atk_count=0
}
friend_count_atk_count(){ //伙伴攻击次数 有装备加成,需要单独处理
if(this.is_master) return
if(this.atk_add_count==0) return
this.atk_count+=1
if(this.atk_count < this.atk_add_count) return
}
do_dead(){
this.do_dead_trigger()
this.Friend_alive_cd=new Timer(this.FIGHTCON.friend_alive_cd)
console.log("[HeroViewComp]:角色死亡",this.hero_uuid)
if(this.is_kalami){
oops.message.dispatchEvent(GameEvent.EXPUP,{exp:50})
oops.message.dispatchEvent(GameEvent.SKILL_STONE_UP,10)
oops.message.dispatchEvent(GameEvent.EQUIP_STONE_UP,10)
}
if(this.is_boss){
oops.message.dispatchEvent(GameEvent.EXPUP,{exp:100})
}
if(this.is_master){
console.log("[HeroViewComp]:英雄死亡")
oops.message.dispatchEvent(GameEvent.FightEnd,{victory:false})
}
}
get_buff(){
let buff=null
if(this.is_master) buff=this.FIGHTCON.hero_buff
if(this.is_friend) buff=this.FIGHTCON.friend_buff
if(this.is_boss) buff=this.FIGHTCON.enemy_buff
if(this.is_kalami) buff=this.FIGHTCON.enemy_buff
if(buff==null) return
return buff
}
get_debuff(){
}
add_debuff(type:number,deV:number,deC:number,deR:number){
let DEBUFF_DOWN=0
let buff_debuff_down=0
let buff=this.get_buff()
if(buff!==null) buff_debuff_down=buff.DEBUFF_DOWN
for(let i=0;i<this.BUFF_DEDOWN.length;i++){
DEBUFF_DOWN+=this.BUFF_DEDOWN[i].value
this.BUFF_DEDOWN[i].count-=1
if(this.BUFF_DEDOWN[i].count<=0){
this.BUFF_DEDOWN.splice(i,1)
}
}
let n_deR=deR-DEBUFF_DOWN-buff_debuff_down // 触发概率
let r=RandomManager.instance.getRandomInt(0,100) // 随机数
console.log("[HeroViewComp]:类型,值,次数,技能概率,实际概率,随机数",type,deV,deC,deR,n_deR,r)
if(r < n_deR) {
// this.BUFFCOMP.tooltip(TooltipTypes.uskill,"*抵抗*")
return
}
switch(type){
case DebuffAttr.BURN:
this.DEBUFF_BURNS.push({value:deV,count:deC+FightSet.BURN_COUNT})
break
case DebuffAttr.DECD:
this.DEBUFF_DECDS.push({value:deV,count:deC})
break
case DebuffAttr.SLOW:
this.DEBUFF_SLOW+=deV
break
case DebuffAttr.FROST:
this.BUFFCOMP.in_iced(deV)
this.DEBUFF_FROST+=deV
break
case DebuffAttr.STUN:
if(this.DEBUFF_STUN>0) return
this.at=0 // 眩晕 cd归零
this.BUFFCOMP.in_yun(deV+FightSet.STUN_TIME) // 眩晕时间
this.DEBUFF_STUN+=deV+FightSet.STUN_TIME // 眩晕时间
// this.is_stop=true
break
case DebuffAttr.DEHP:
this.hp_max-=deV/100*this.hp_max
if(this.hp-this.hp_max>0) this.hp=this.hp_max
break
case DebuffAttr.DEATK: //99为具体数字 并且局内永久生效,其他为百分比
if(deC == 99){
this.ap-=deV
}else{
this.DEBUFF_DEATKS.push({value:deV,count:deC})
}
break
case DebuffAttr.DECOUNT:
this.atk_count-=deV
if(this.atk_count<0) this.atk_count=1
break
case DebuffAttr.BACK:
if(this.fac==FacSet.MON) {
let tx=this.node.position.x+50
if(tx > 320) tx=320
tween(this.node).to(0.3, { position:v3(tx,this.node.position.y,0)}).start()
}
break
}
console.log("[HeroViewComp]:debuffs",type,deV,deC,deR,this.DEBUFF_BURNS)
}
add_buff(buff:number,count:number,type:number){
switch(type){
case BuffAttr.DEF:
this.BUFF_DEFS.push({value:buff,count:count})
break
case BuffAttr.ATK:
this.BUFF_ATKS.push({value:buff,count:count})
break
case BuffAttr.ATK_CD:
this.BUFF_CDS.push({value:buff,count:count})
break
case BuffAttr.DEBUFF_DOWN:
this.BUFF_DEDOWN.push({value:buff,count:count})
break
}
}
do_atked(remainingDamage:number,
crit:number=0,crit_d:number=0,
burn_count:number=0,burn_value:number=0,
stun_time:number=0,stun_ratto:number=0,
frost_time:number=0,frost_ratto:number=0,
){
this.do_atked_trigger()
if(burn_count>0){
this.add_debuff(DebuffAttr.BURN,burn_value,burn_count,100)
}
if(stun_time>0){
this.add_debuff(DebuffAttr.STUN,stun_time,1,stun_ratto)
}
if(this.check_shield()) return
if(this.check_dodge()) return
let is_crit = this.check_crit(crit)
if(this == null) return;
let damage = this.count_damage(remainingDamage)
if(is_crit) {
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE+crit_d)/100))
}
this.hp -= damage;
if(this.hp <= 0) {
if(this == null) return;
this.is_dead=true
this.BUFFCOMP.dead()
this.do_dead()
console.log("[HeroViewComp]:dead,fac => "+(this.fac==FacSet.HERO?"hero":"monster"))
if(this.ent == null) return;
if(this.fac ==FacSet.HERO){
this.to_grave()
}else{
this.ent.destroy();
}
}
this.BUFFCOMP.vmdata_update(true)
this.showDamage(damage, is_crit);
}
//伤害计算 debuff 易伤
count_damage(remainingDamage:number){
let damage = 0;
let Burn = 0;
let def = 0;
for(let i=0;i<this.DEBUFF_BURNS.length;i++){
Burn+=this.DEBUFF_BURNS[i].value
this.DEBUFF_BURNS[i].count-=1
if(this.DEBUFF_BURNS[i].count<=0){
this.DEBUFF_BURNS.splice(i,1)
}
}
for(let i=0;i<this.BUFF_DEFS.length;i++){
def+=this.BUFF_DEFS[i].value
this.BUFF_DEFS[i].count-=1
if(this.BUFF_DEFS[i].count<=0){
this.BUFF_DEFS.splice(i,1)
}
}
// buff 防御 即免伤
let buff_def=0
let buff=this.get_buff()
if(buff!==null) buff_def=buff.DEF
damage=remainingDamage*(100-(buff_def+def)+Burn)/100
console.log("[HeroViewComp]:最终伤害,敌人伤害值,免伤,防御,易伤",damage,remainingDamage,buff_def,def,Burn)
return Math.floor(damage)
}
count_debuff(){
}
count_buff(){
}
check_shield(){
if(this.shield>0){
this.shield -= 1
this.BUFFCOMP.tooltip(TooltipTypes.uskill,"*吸收*");
if (this.shield <= 0) {
if(this == null) return;
this.BUFFCOMP.show_shield(false);
}
return true
}
return false
}
check_dodge(){
if(this.dod > 0){
let random = Math.random()*100
if(random < this.dod) {
this.BUFFCOMP.tooltip(TooltipTypes.uskill,"*闪避*");
return true
}
}
return false
}
check_crit(crit:number=0){
if(this.crit_no) return false
if(crit > 0){
let random = Math.random()*100
if(random < crit) {
console.log("[HeroViewComp]:crit",crit,random)
return true
}
}
console.log("[HeroViewComp]:crit",crit)
return false
}
// dead(){
// this.BUFFCOMP.dead()
// this.to_drop()
// }
do_dead_trigger(){
if(this.is_dead||this.fac==FacSet.MON||this.is_master) return
let count = 1
if(this.double_dead) {
console.log("[HeroViewComp]:double_dead")
count =2
}
for(let i=0;i<count;i++){
console.log("[HeroViewComp]:dead"+i+"次")
}
}
do_atked_trigger(){
if(this.is_dead||this.fac==FacSet.MON||this.is_master) return
let count = 1
if(this.double_atked) {
console.log("[HeroViewComp]:double_atked")
count =2
}
}
to_grave(){
tween(this.node).to(0.5, { position:v3(-900,this.node.position.y+300,0)},{
onComplete: (target?: object) => {
this.node.setPosition(-900,this.node.position.y-300,0)
}
}
).start()
}
to_alive(){
this.is_dead=false
this.hp=this.hp_max*(100+this.buff_hp)/100
this.BUFFCOMP.vmdata_update(true)
this.node.setPosition(HeroPos[this.fight_pos].pos)
this.BUFFCOMP.heathed()
}
//掉落物品
to_drop(){
// let Drops = getMonsterDrops(1, MonsterType.Normal, 1.2);
// if(this.is_boss) Drops =getMonsterDrops(1, MonsterType.Elite, 1.2);
// if(this.is_big_boss) Drops =getMonsterDrops(1, MonsterType.Boss, 1.2);
}
to_console(value:any,value2:any=null,value3:any=null){
console.log("["+this.scale+this.hero_name+']'+value,value2,value3)
}
reset() {
this.is_dead = false;
const collider = this.getComponent(Collider2D);
if (collider) {
collider.off(Contact2DType.BEGIN_CONTACT);
}
this.scheduleOnce(() => {
this.node.destroy();
}, 0.1);
}
playSkillEffect(skill_id:number) {
let skill = SkillSet[skill_id]
switch(skill.act){
case "max":
this.as.max()
this.BUFFCOMP.max_show(skill.fname)
this.BUFFCOMP.tooltip(TooltipTypes.skill,skill.name,skill_id)
break
case "atk":
this.as.atk()
break
}
}
exp_up(e:any,data:any){
if(this.fac==FacSet.MON) return
console.log("[HeroViewComp]:经验提高",data.exp)
this.exp+=data.exp
this.next_exp=getUpExp(this.lv)
let diff=this.exp-this.next_exp
if(diff >= 0){
this.exp=diff
this.to_update()
}
this.BUFFCOMP.vmdata_update(true) //简易更新vmdata
}
to_update(){
this.add_ap(getApIncrease(this.hero_uuid,this.lv,this.lv+1))
this.add_hp_max(getHpIncrease(this.hero_uuid,this.lv,this.lv+1))
this.lv+=1
this.next_exp=getUpExp(this.lv)
this.BUFFCOMP.vmdata_update()
this.BUFFCOMP.lv_up()
this.BUFFCOMP.tooltip(TooltipTypes.lvup)
//@todo 需要添加 升级动画
}
/** 显示伤害数字 */
showDamage(damage: number, isCrit: boolean) {
this.damageQueue.push({
damage,
isCrit,
delay: this.damageInterval
});
}
ex_show(text:string){
switch(text){
case "blue":
this.BUFFCOMP.max_show("max_blue")
break
case "red":
this.BUFFCOMP.max_show("max_red")
break
}
}
/** 处理伤害队列 */
private processDamageQueue() {
if (this.isProcessingDamage || this.damageQueue.length === 0) return;
this.isProcessingDamage = true;
const damageInfo = this.damageQueue.shift()!;
this.showDamageImmediate(damageInfo.damage, damageInfo.isCrit);
// 设置延时处理下一个伤害
this.scheduleOnce(() => {
this.isProcessingDamage = false;
}, this.damageInterval);
}
/** 立即显示伤害效果 */
private showDamageImmediate(damage: number, isCrit: boolean) {
this.BUFFCOMP.in_atked();
this.atked_count++;
if (isCrit) {
this.BUFFCOMP.tooltip(TooltipTypes.crit, damage.toFixed(0), damage);
// console.log("暴击伤害:" + damage);
} else {
this.BUFFCOMP.tooltip(TooltipTypes.life, damage.toFixed(0), damage);
// console.log("普通伤害:" + damage);
}
}
}