Files
heros/assets/script/game/hero/HeroViewComp.ts
2025-01-02 00:00:05 +08:00

674 lines
23 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.

/*
* @Author: dgflash
* @Date: 2021-11-18 17:42:59
* @LastEditors: dgflash
* @LastEditTime: 2022-08-17 12:36:18
*/
import { Vec3, _decorator , v3,Collider2D,Contact2DType,Label,RigidBody2D ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game} 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 { Hero } from "./Hero";
import { HeroModelComp } from "./HeroModelComp";
import { BoxSet, GameSet } from "../common/config/BoxSet";
import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { Skill } from "../skills/Skill";
import { Timer } from "../../../../extensions/oops-plugin-framework/assets/core/common/timer/Timer";
import { SkillCom } from "../skills/SkillCom";
import { SkillSet } from "../common/config/SkillSet";
import { Tooltip } from "../skills/Tooltip";
import { RandomManager } from "../../../../extensions/oops-plugin-framework/assets/core/common/random/RandomManager";
import { TimerManager } from "../../../../extensions/oops-plugin-framework/assets/core/common/timer/TimerManager";
import { HeroSet } from "../common/config/heroSet";
import { BuffComp } from "./BuffComp";
const { ccclass, property } = _decorator;
/** 角色显示组件 */
@ccclass('HeroViewComp') // 定义为 Cocos Creator 组件
@ecs.register('HeroView', false) // 定义为 ECS 组件
export class HeroViewComp extends CCComp {
BUFFCOMP:any=null!;
enemy_pos:Vec3=null!;
enemy:any=null!;
as: HeroSpine = null!;
anm_timer:Timer = new Timer(0.3);
anm_name="idle"
status:String = "idle"
hero_uuid:number = 1001;
hero_name : string = "hero";
lv:number =1;
scale: number = 1; /** 角色阵营 1hero -1 :mon */
type: number = 0; /**角色类型 0近战 1 远程 2 辅助 */
box_group:number = BoxSet.HERO;
atk_range:number = 150;
is_dead:boolean = false; //是否摧毁
is_stop:boolean = false;
is_atking:boolean = false;
hp: number = 100; /** 血量 */
hp_max: number = 100; /** 最大血量 */
rhp_max: number = 100;
hp_speed: number = 0; //每秒回复量
pw: number = 0; /**能量**/
pwm: number = 15; /** 能量最大值 */
pws: number = 1; //能量回复速度每0.1秒回复量
apw:number=0;
uapw:number=0;
cpw:number=0;
dopw:number=0;
dpw:number=0;
pwt:Timer = new Timer(1); //计时器
sk1:number = 9001;
sk2:number = 1001;
sk3:number = 1001;
akr:number=0; //攻击触发机率
uar:number=0; //受伤触发机率
dgr:number=0; //闪避触发机率
crr:number=0; //暴击触发机率
akc:number=0; //攻击次数触发
uac:number=0; //受伤次数触发
dgc:number=0; //闪避次数触发
crc:number=0; //暴击次数触发
aexp:number=0; //攻击经验
uaexp:number=0; //受伤经验
cexp:number=0; //暴击经验 */
doexp:number=0; //闪避经验 */
dexp:number=0; //死亡经验 */
ap: number = 10; /**攻击力 */
ap_max: number = 0;
ap_buff: number = 0;
ap_buffs:any = [];
// atk_speed: number = 1;
cd: number = 1.3; /**攻击速度 攻击间隔 */
dis: number = 80;
at: number = 0; /** 冷却时间 */
def: number = 0; //防御
def_max: number = 0;
vun: number = 0; //易伤
crit: number = 0; //暴击率
crit_max: number = 0;
crit_add: number = 0;//暴击伤害加成
dodge: number = 10; //闪避率
dodge_max: number = 10; //闪避率
shield:number = 0; //护盾,免伤1次减1
speed: number = 100; /** 角色移动速度 */
ospeed: number = 100; /** 角色初始速度 */
atk_count: number = 0;
atked_count: number = 0;
dodge_count: number = 0;
crit_count: number = 0;
stop_cd: number = 0.5; /*停止倒计时*/
yun_cd: number = 0.5; //眩晕倒计时
ice_cd: number = 0.5; //冰冻倒计时
dir_y:number = 0;
speek_time:number = 0;
onLoad() {
this.as = this.getComponent(HeroSpine);
} /** 视图层逻辑代码分离演示 */
start () {
this.as.idle()
this.BUFFCOMP=this.node.getComponent(BuffComp);
/** 方向 */
this.node.setScale(this.scale,1);
this.node.getChildByName("top").setScale(this.scale,1);
/** 显示角色血量 */
this.node.getChildByName("top").getChildByName("hp").active = true;
// this.node.getChildByName("shielded").active = false;
// this.node.getChildByName("top").setScale(this.scale,1);
// this.node.getChildByName("atk").setScale(this.scale,1);
// this.node.getChildByName("atk").getComponent(Label).string = this.ap.toString();
// this.node.getChildByName("hp_max").setScale(this.scale,1);
// this.node.getChildByName("hp_max").getComponent(Label).string=this.hp_max.toString();
// this.BoxRang.getComponent(BoxRangComp).box_group = this.box_group;
// this.BoxRang.getComponent(BoxRangComp).atk_range = this.ap_range
// this.BoxRang.getComponent(BoxRangComp).offset_x = 300;
// console.log("monseter ",this.BoxRang);
// console.log("monseter ",this.BoxRang);
// 注册单个碰撞体的回调函数
let collider = this.getComponent(Collider2D);
collider.group = this.box_group;
// console.log("hero collider ",this.scale,collider);
if (collider) {
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
// collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
collider.on(Contact2DType.PRE_SOLVE, this.onPreSolve, this);
// collider.on(Contact2DType.POST_SOLVE, this.onPostSolve, this);
}
}
onBeginContact (selfCollider: Collider2D, otherCollider: Collider2D) {
if(otherCollider.tag==BoxSet.SKILL_TAG &&selfCollider.tag!=BoxSet.SKILL_TAG){
if(selfCollider.group != otherCollider.group){
let skill = otherCollider.node.getComponent(SkillCom)!;
// console.log('onPostSolve',skill);
if(this.hp <= 0 ) return
this.check_uatk(skill);
}
}
}
onEndContact (selfCollider: Collider2D, otherCollider: Collider2D) {
}
onPreSolve (selfCollider: Collider2D, otherCollider: Collider2D) {
let self_x = selfCollider.node.position.x;
let other_x = otherCollider.node.position.x;
if(selfCollider.group == otherCollider.group&&selfCollider.tag==otherCollider.tag){
if(selfCollider.group==BoxSet.HERO){
if(otherCollider.node.getComponent(HeroViewComp).type == this.type && self_x < other_x && Math.abs(other_x-self_x) < 50 ){
// this.node.setSiblingIndex(otherCollider.node.getSiblingIndex()-10)
this.stop_cd = 0.1;
}
}
if(selfCollider.group==BoxSet.MONSTER){
if(otherCollider.node.getComponent(HeroViewComp).type == this.type && self_x > other_x && Math.abs(other_x-self_x) < 50 ){
// this.node.setSiblingIndex(otherCollider.node.getSiblingIndex()-10)
this.stop_cd = 0.1;
}
}
}
if(selfCollider.group != otherCollider.group&&otherCollider.tag == 0){
this.stop_cd = 0.1;
this.is_atking=true
}
}
onPostSolve (selfCollider: Collider2D, otherCollider: Collider2D) {
}
update(dt: number){
if(!smc.mission.play||smc.mission.pause) return
if(this.is_dead) {
if(!this.in_grave()) this.to_grave()
return
}
if (this.pwt.update(dt)) {
this.pw+=this.pws
}
this.check_power()
this.check_atk_counts()
this.check_enemy_alive()
this.check_mission_buff()
if(this.ice_cd > 0){ this.ice_cd -=dt; return }
if(this.yun_cd > 0){ this.yun_cd -=dt; return }
this.at += dt;
this.in_stop(dt);
this.in_atk(dt);
this.move(dt);
}
check_mission_buff(){
this.ap_max=(100+smc.vmdata.mission.ap)/100*this.ap
this.crit_max=(100+smc.vmdata.mission.crit)/100*this.crit
this.def_max=(100+smc.vmdata.mission.def)/100*this.def
this.dodge_max=(100+smc.vmdata.mission.dodge)/100*this.dodge
this.rhp_max=(100+smc.vmdata.mission.hp)/100*this.hp_max
if(this.box_group == BoxSet.MONSTER){
this.ap_max=(100+smc.vmdata.mission.map)/100*this.ap
this.crit_max=(100+smc.vmdata.mission.mcrit)/100*this.crit
this.def_max=(100+smc.vmdata.mission.mdef)/100*this.def
this.dodge_max=(100+smc.vmdata.mission.mdodge)/100*this.dodge
this.rhp_max=(100+smc.vmdata.mission.mhp)/100*this.hp_max
}
}
check_enemy_alive(){
let dir = 320
let enemys=smc.enemy_pos
this.enemy = v3(720,this.node.position.y)
if(this.box_group == BoxSet.MONSTER){
enemys=smc.hero_pos
this.enemy=v3(-720,this.node.position.y)
}
for (let i = 0; i < enemys.length; i++) {
let ho:any = enemys[i];
let x=Math.abs(ho.x-this.node.position.x)
if(x < dir){
dir = x
this.enemy = ho
}
}
if(dir < this.dis){
this.is_atking=true
if(this.dis-dir > 80 &&this.type > 0 ) this.stop_cd = 0.1
if(dir < 65 &&this.type == 0 ) this.stop_cd = 0.1
}else{
this.is_atking=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")
}
}
//移动
move(dt: number){
if(this.stop_cd > 0||smc.mission.is_victory||smc.mission.is_defeat){
this.status_change("idle")
return
}
if (this.node.position.x >= 300 && this.scale==1) {
return;
}
if(this.scale===-1&&this.node.position.x <= -300){
return;
}
this.status_change("move")
// if(this.enemy){
// return
// }
this.node.setPosition(this.node.position.x+dt*this.speed*this.scale, this.node.position.y+dt*this.dir_y, this.node.position.z);
}
skill_pos(){
return v3(0,35)
}
get_enemy_pos(){
let pos =this.skill_pos()
let t_pos:Vec3 = v3(720,0)
if(this.enemy){
t_pos = v3(this.enemy.x-this.node.position.x,this.enemy.y-this.node.position.y)
}
return {pos,t_pos}
}
get_hero_pos(hero:any){
let pos =this.skill_pos()
let t_pos:Vec3 = v3(720,0)
if(!hero.node.isValid){
return
}else{
t_pos = v3(hero.node.position.x-this.node.position.x,hero.node.position.y-(this.node.position.y+pos.y))
}
return {pos,t_pos}
}
//受伤判断
check_uatk(skill:any){
if(this.shield > 0){
this.shield -= 1
if(this.shield <= 0){
this.shield = 0
this.node.getChildByName("shielded").active = false
}
return
}
if(this.check_dodge()) return
this.in_atked();
let d=this.def/skill.ap
if(d > 1) d = 1
let l_hp=skill.ap*(1-d*GameSet.DEF_RATE) //防御最高减免伤害比率计算
if(skill.is_crit){
l_hp = l_hp * (150+skill.crit_add)/100
}
l_hp=Math.ceil(l_hp)
this.hp_less(l_hp,skill.is_crit);
}
//能量判断
check_power(){
if(this.pw >= this.pwm){
this.pw = 0
this.BUFFCOMP.max_show()
this.handle_skill(this.sk2)
return true
}else{
return false
}
}
//暴击判断
/**
* 检查是否触发暴击,并执行相应的暴击效果。
* @returns {boolean} 如果触发暴击则返回 true否则返回 false。
* 该方法首先通过 RandomManager 获取一个随机数如果该随机数小于当前暴击最大值crit_max
* 则触发暴击效果,包括显示暴击提示、增加暴击计数、增加暴击经验以及增加暴击威力。
* 如果未触发暴击,则直接返回 false。
*/
check_crit():boolean
{
let i = RandomManager.instance.getRandomInt(0,100,3)
if(i < this.crit_max){
this.BUFFCOMP.tooltip(5,"*会心一击*");
this.crit_count += 1
this.exp_add(this.cexp) // 暴击经验
this.power_add(this.cpw)
return true
}else{
return false
}
}
//闪避判断
/**
* 检查并处理角色的闪避逻辑。
* 生成一个随机数,如果小于角色的最大闪避值,则触发闪避效果,增加经验、能量,并更新闪避计数。
* @returns {boolean} 如果触发闪避则返回true否则返回false。
*/
check_dodge():boolean
{
let i = RandomManager.instance.getRandomInt(0,100,3)
if(i < this.dodge_max){
// console.log("闪避触发: i="+i+":dodge="+dodge);
this.BUFFCOMP.tooltip(5,"闪避");
this.exp_add(this.doexp) // 闪避经验
this.power_add(this.dopw)
this.dodge_count += 1
return true
}else{
return false
}
}
/**
* 检查并处理角色的攻击、闪避、暴击和受伤计数。
* 当计数达到一定值时,会触发相应的技能效果,并重置计数。
* 触发效果包括激活名为"max"的节点并在0.8秒后关闭。
* 使用handle_skill方法处理触发的技能。
*/
check_atk_counts() {
if (this.atk_count >= this.akc) {
this.atk_count = 0
// console.log("atk_count 清零:"+this.atk_count);
let i = RandomManager.instance.getRandomInt(0,100,3)
// console.log("攻击判断: i="+i+":akr="+this.akr);
if(i < this.akr){
// console.log("攻击触发: i="+i+":akr="+this.akr);
this.BUFFCOMP.max_show()
this.handle_skill(this.sk3)
}
}
if(this.dodge_count >= this.dgc){
this.dodge_count = 0
// console.log("dodge_count 清零:"+this.dodge_count);
let i = RandomManager.instance.getRandomInt(0,100,3)
// console.log("闪避判断: i="+i+":dgr="+this.dgr);
if(i < this.dgr){
// console.log("闪避触发: i="+i+":dgr="+this.dgr);
this.BUFFCOMP.max_show()
this.handle_skill(this.sk3)
}
}
if(this.crit_count >= this.crc){
this.crit_count = 0
// console.log("crit_count 清零:"+this.crit_count);
let i = RandomManager.instance.getRandomInt(0,100,3)
// console.log("暴击判断: i="+i+":crr="+this.crr);
if(i < this.crr){
// console.log("暴击触发: i="+i+":crr="+this.crr);
this.BUFFCOMP.max_show()
this.handle_skill(this.sk3)
}
}
if(this.atked_count >= this.uac){
this.atked_count = 0
let i = RandomManager.instance.getRandomInt(0,100,3)
// console.log("受伤判断i="+i+":akr="+this.uar);
if(i < this.uar){
// console.log("受伤触发: i="+i+":uar="+this.uar);
this.BUFFCOMP.max_show()
this.handle_skill(this.sk3)
}
}
}
in_atk(dt: number) {
if(this.at >= this.cd){
if(this.is_atking){
this.at = 0;
this.atk_count++
this.exp_add(this.aexp) //攻击经验
this.power_add(this.apw)
// console.log("cd:"+this.cd);
this.as.atk();
this.scheduleOnce(()=>{
this.shoot_enemy(this.sk1)
},0.3)
}
}
}
//使用max_skill
handle_skill(skill:number){
this.as.max()
this.at = 0;
this.BUFFCOMP.tooltip(3,SkillSet[skill].name,skill);
switch (SkillSet[skill].tg) {
case 0: //自己
this.to_add_buff(this.node.getComponent(HeroViewComp),skill)
break;
case 1: //伙伴
this.push_least_buff(skill)
break;
case 2: //自己和伙伴
this.to_add_buff(this.node.getComponent(HeroViewComp),skill)
this.push_least_buff(skill)
break;
case 3: //敌人
this.shoot_enemy(skill)
break;
case 4: //敌人和自己
this.to_add_buff(this.node.getComponent(HeroViewComp),skill)
this.shoot_enemy(skill)
break;
}
}
shoot_enemy(sk:number,y:number=0,x:number=0){
// console.log("mon shoot_enemy");
let skill = ecs.getEntity<Skill>(Skill);
let {pos,t_pos}=this.get_enemy_pos()
pos.y=pos.y + y
pos.x=pos.x + x
let is_crit=this.check_crit()
skill.load(pos,this.box_group,this.node,sk,this.ap_max,t_pos,is_crit,this.crit_add);
console.log(this.scale+this.hero_name+"使用技能:"+sk);
}
to_add_buff(hero:any,sk:number){
let skill = ecs.getEntity<Skill>(Skill);
let {pos,t_pos}=this.get_hero_pos(hero)
console.log("to_add_buff:"+hero.hero_name+" "+sk);
let is_crit=this.check_crit()
skill.load(pos,this.box_group,this.node,sk,this.ap_max,t_pos,is_crit,this.crit_add);
if(SkillSet[sk].hp > 0){ //buff加血
let increase_hp=Math.floor(SkillSet[sk].hp*this.ap_max)
hero.add_hp(increase_hp)
}
if(SkillSet[sk].ap > 0){ //buff加攻击
let increase_atk=Math.floor(SkillSet[sk].ap*this.ap_max)
hero.add_ap(increase_atk,SkillSet[sk].bsd)
}
if(SkillSet[sk].shield > 0){ //buff护盾
hero.add_shield(SkillSet[sk].shield)
}
}
push_least_buff(skill:number){
let heros:any = ecs.query(ecs.allOf(HeroModelComp));
let least_hp:number=0
let t_hero:any= null
if (heros.length > 0) {
if(SkillSet[skill].type==92){ //随机添加buff
let i = RandomManager.instance.getRandomInt(0,heros.length-1,3)
while(!heros[i].HeroView){
i = RandomManager.instance.getRandomInt(0,heros.length-1,3)
if(heros[i].HeroView){
break
}
}
this.to_add_buff(heros[i].HeroView,skill)
}else{
for (let i = 0; i < heros.length; i++) {
if(!heros[i].HeroView) continue
let hero = heros[i].HeroView;
if(SkillSet[skill].type==91){ //血量最少单体
if((hero.rhp_max-hero.hp) > least_hp){
least_hp = (hero.rhp_max-hero.hp)
t_hero = hero
}
}else{ //群体
this.to_add_buff(hero,skill)
}
}
if(t_hero){ //血量最少单体
this.to_add_buff(t_hero,skill)
}
}
}
}
exp_add(exp:number=0){
if(this.box_group==BoxSet.HERO){
smc.vmdata.mission.exp +=exp
}
if(this.box_group==BoxSet.MONSTER){
smc.vmdata.mission.mexp +=exp
}
}
power_add(p:number){
this.pw+= p
}
/**
* 增加英雄的行动点数AP
* @param ap 要增加的行动点数。
* @param time 可选参数表示增加行动点数的时间默认为0。
*/
add_ap(ap: number,time:number=0){
this.ap += ap;
}
add_shield(shield:number){
this.shield =shield
console.log("shield:",shield);
}
add_hp(hp: number=0){
this.BUFFCOMP.heathed();
this.hp+=hp;
if(this.hp > this.rhp_max){
this.hp = this.rhp_max;
}
this.BUFFCOMP.tooltip(2,hp.toFixed(0));
}
add_hp_max(hp: number=0){
this.BUFFCOMP.show_buff(1)
this.hp += hp/100*this.hp_max;
}
hp_less(hp: number,is_crit:boolean=false){
if(this.is_dead){
return;
}
this.hp -= hp;
if(is_crit){
this.BUFFCOMP.tooltip(4,hp.toFixed(0),250);
}else{
this.BUFFCOMP.tooltip(1,hp.toFixed(0),250);
}
if(this.hp > this.rhp_max){
this.hp = this.rhp_max;
}
if(this.hp <= 0){
this.dead();
this.to_grave()
this.is_dead = true;
// setTimeout(() => {
// this.ent.destroy();
// }, 15);
}
}
/** 静止时间 */
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;
}
}
}
in_atked() {
this.BUFFCOMP.in_atked()
// this.as.atked();
this.atked_count++;
this.exp_add(this.uaexp)
this.power_add(this.uapw)
}
dead(){
this.BUFFCOMP.dead()
this.exp_add(this.dexp)
this.power_add(this.dpw)
}
//进入墓地
to_grave(){
let pos =v3(-999,this.node.position.y)
if(this.box_group == BoxSet.MONSTER){
pos =v3(999,this.node.position.y)
smc.vmdata.mission.mdead +=1
}else{
smc.vmdata.mission.dead +=1
}
this.node.setPosition(pos)
}
//是否在墓地
in_grave(){
return this.node.position.x < -900 || this.node.position.x > 900;
}
to_alive(){
let pos =v3(HeroSet.StartPos[this.type],this.node.position.y,this.node.position.z)
this.node.setPosition(pos)
}
reset() {
this.is_dead = false;
this.node.destroy();
}
}