import { Vec3, _decorator , v3,Collider2D,Contact2DType,Label ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Color, BoxCollider2D, UITransform} 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/GameSet"; import { smc } from "../common/SingletonModuleComp"; import { EAnmConf, SkillSet,} from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; import { GameEvent } from "../common/config/GameEvent"; import { TooltipTypes } from "../common/config/GameSet"; import { Attrs, } from "../common/config/HeroAttrs"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { Tooltip } from "../skill/Tooltip"; import { timedCom } from "../skill/timedCom"; import { HeroInfo, HType } from "../common/config/heroSet"; const { ccclass, property } = _decorator; /** 角色显示组件 */ export interface BuffInfo { value: number; remainTime?: number; } @ccclass('HeroViewComp') // 定义Cocos Creator 组件 @ecs.register('HeroView', false) // 定义ECS 组件 export class HeroViewComp extends CCComp { // ==================== View 层属性(表现相关)==================== as: HeroSpine = null! status:String = "idle" scale: number = 1; // 显示方向 box_group:number = BoxSet.HERO; // 碰撞组 usePower:boolean = false; useMp:boolean = false; // ==================== UI 节点引用 ==================== private top_node: Node = null!; // ==================== 直接访问 HeroAttrsComp ==================== get model() { return this.ent.get(HeroAttrsComp); } private damageQueue: Array<{ damage: number, isCrit: boolean, delay: number, anm:string, }> = []; private isProcessingDamage: boolean = false; private damageInterval: number = 0.01; // 伤害数字显示间隔 onLoad() { this.as = this.getComponent(HeroSpine); //console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON) this.on(GameEvent.FightEnd,this.do_fight_end,this) const collider = this.node.getComponent(BoxCollider2D); this.scheduleOnce(()=>{ if (collider) collider.enabled = true; // 先禁 },0.1) // let anm = this.node.getChildByName("anm") // anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8); } /** 视图层逻辑代码分离演示 */ start () { this.as.idle() // 初始化 UI 节点 this.initUINodes(); /** 方向 */ this.node.setScale(this.scale,1); this.top_node.setScale(this.scale,1); // if(this.model && this.model.is_boss){ // this.top_node.position=v3(this.node.position.x,this.node.position.y+70,0) // } /* 显示角色血*/ this.top_node.getChildByName("hp").active = true; this.usePower=HeroInfo[this.model.hero_uuid].type==HType.warrior||HeroInfo[this.model.hero_uuid].type==HType.assassin; this.useMp=HeroInfo[this.model.hero_uuid].type==HType.mage||HeroInfo[this.model.hero_uuid].type==HType.remote||HeroInfo[this.model.hero_uuid].type==HType.support; this.top_node.getChildByName("pow").active = this.usePower; this.top_node.getChildByName("mp").active = this.useMp; } /** 初始化 UI 节点引用 */ private initUINodes() { this.top_node = this.node.getChildByName("top"); let hp_y = this.node.getComponent(UITransform).height; this.top_node.setPosition(0, hp_y, 0); } /** * View 层每帧更新 * 注意:数据更新逻辑已移到 HeroAttrSystem,这里只负责显示 */ update(dt: number){ if(!smc.mission.play || smc.mission.pause) return; // 添加安全检查,防止在实体销毁过程中访问null的model if (!this.model) return; if(this.model.is_dead) return; // ✅ View 层职责:处理表现相关的逻辑 this.processDamageQueue(); // 伤害数字显示队列 // ✅ 更新 UI 显示(数据由 HeroAttrSystem 更新) this.hp_show(this.model.hp, this.model.Attrs[Attrs.HP_MAX]); if(this.useMp) this.mp_show(this.model.mp, this.model.Attrs[Attrs.MP_MAX]); if(this.usePower) this.pow_show(this.model.pow, this.model.Attrs[Attrs.POW_MAX]); this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]); } /** 显示护盾 */ private show_shield(shield: number = 0, shield_max: number = 0) { if(!this.top_node.active) return let shield_progress = shield / shield_max; this.node.getChildByName("shielded").active = shield > 0; this.top_node.getChildByName("shield").active = shield > 0; this.top_node.getChildByName("shield").getComponent(ProgressBar).progress = shield_progress; this.scheduleOnce(() => { this.top_node.getChildByName("shield").getChildByName("pb").getComponent(ProgressBar).progress = shield_progress; }, 0.15); } /** 显示血量 */ private hp_show(hp: number, hp_max: number) { if(hp==hp_max) { this.top_node.active=false; return } this.top_node.active=true let hp_progress = hp / hp_max; this.top_node.getChildByName("hp").getComponent(ProgressBar).progress = hp_progress; this.scheduleOnce(() => { this.top_node.getChildByName("hp").getChildByName("hpb").getComponent(ProgressBar).progress = hp_progress; }, 0.15); } /** 显示魔法值 */ private mp_show(mp: number, mp_max: number) { if(!this.top_node.active) return this.top_node.getChildByName("mp").getComponent(ProgressBar).progress = mp / mp_max; this.scheduleOnce(() => { this.top_node.getChildByName("mp").getChildByName("mpb").getComponent(ProgressBar).progress = mp / mp_max; }, 0.15); } private pow_show(pow: number, pow_max: number) { if(!this.top_node.active) return this.top_node.getChildByName("pow").getComponent(ProgressBar).progress = pow / pow_max; this.scheduleOnce(() => { this.top_node.getChildByName("pow").getChildByName("mpb").getComponent(ProgressBar).progress = pow / pow_max; }, 0.15); } /** 升级特效 */ private lv_up() { var path = "game/skill/buff/buff_lvup"; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); node.parent = this.node; } /** 攻击力提升特效 */ private ap_up() { var path = "game/skill/buff/buff_apup"; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); node.parent = this.node; } /** 显示 Buff 特效 */ private show_do_buff(name: string) { var path = "game/skill/buff/" + name; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); let pos = v3(this.node.position.x, this.node.position.y + 20, this.node.position.z); node.parent = this.node.parent; node.setPosition(pos); } /** 受击特效 */ private in_atked(anm: string = "atked", scale: number = 1) { var path = "game/skill/end/" + anm; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); node.setScale(node.scale.x * scale, node.scale.y); node.setPosition(this.node.position.x, this.node.position.y+8, this.node.position.z); node.parent = this.node.parent; } /** 冰冻特效 */ private in_iced(t: number = 1, ap: number = 0) { var path = "game/skill/buff/buff_iced"; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); node.getComponent(timedCom).time = t; node.getComponent(timedCom).ap = ap; node.parent = this.node; } /** 眩晕特效 */ private in_yun(t: number = 1, ap: number = 0) { var path = "game/skill/buff/buff_yun"; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); let height = this.node.getComponent(UITransform).height; node.setPosition(v3(0, height)); node.getComponent(timedCom).time = t; node.getComponent(timedCom).ap = ap; node.parent = this.node; } /** 技能提示 */ private tooltip(type: number = 1, value: string = "", s_uuid: number = 1001, y: number = 90) { let tip = ecs.getEntity(Tooltip); let pos = v3(0, 60); pos.y = pos.y + y; tip.load(pos, type, value, s_uuid, this.node); } /** 血量提示(伤害数字) */ private hp_tip(type: number = 1, value: string = "", s_uuid: number = 1001, y: number = 100) { let tip = ecs.getEntity(Tooltip); let x = this.node.position.x; let ny = this.node.getComponent(UITransform).height + y; let pos = v3(x, ny, 0); tip.load(pos, type, value, s_uuid, this.node.parent); } /** 治疗特效 */ private heathed() { var path = "game/skill/buff/heathed"; var prefab: Prefab = oops.res.get(path, Prefab)!; var node = instantiate(prefab); node.parent = this.node; } // 注意:BaseUp 逻辑已移到 HeroAttrSystem.update() // 注意:updateTemporaryBuffsDebuffs 逻辑已移到 HeroAttrSystem.update() do_fight_end(){ this.as.do_buff() } get isActive() { return this.ent.has(HeroViewComp) && this.node?.isValid; } /** 状态切换(动画) */ status_change(type:string){ this.status = type; if(type === "idle"){ this.as.idle(); } else if(type === "move"){ this.as.move(); } } add_shield(shield:number){ // 护盾数据更新由 Model 层处理,这里只负责视图表现 if(this.model && this.model.shield>0) this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]); } health(hp: number = 0, is_num:boolean=true) { // 生命值更新由 Model 层处理,这里只负责视图表现 this.heathed(); if(this.model) { this.hp_show(hp, this.model.Attrs[Attrs.HP_MAX]); } } /** * 死亡视图表现 * 由 HeroAtkSystem 调用,只负责视觉效果和事件通知 */ do_dead(){ // 添加安全检查 if (!this.model) return; // 防止重复触发 if(this.model.is_count_dead) return; this.model.is_count_dead = true; // 防止重复触发,必须存在防止重复调用 // 播放死亡特效 this.as.dead(); // 根据阵营触发不同事件 if(this.model.fac === FacSet.MON){ // 怪物死亡:延迟触发掉落(暂时不发事件,等待实现) // this.scheduleOnce(() => { // oops.message.dispatchEvent(GameEvent.MonsterDead, { // hero_uuid: this.model.hero_uuid, // position: this.node.position // }); // }, 0.2); } else if(this.model.fac === FacSet.HERO){ // 英雄死亡:延迟触发死亡事件 this.scheduleOnce(() => { oops.message.dispatchEvent(GameEvent.HeroDead, { hero_uuid: this.model.hero_uuid }); }, 0.2); } } do_atked(damage:number,isCrit:boolean,s_uuid:number){ if (damage <= 0) return; // 视图层表现 let SConf=SkillSet[s_uuid] this.back() this.showDamage(damage, isCrit, SConf.DAnm); // 暴击状态由战斗系统内部处理, DAnm和EAnm共用设定数组 } //后退 back(){ if(this.model.fac==FacSet.MON) { let tx=this.node.position.x+30 if(tx > 320) tx=320 tween(this.node).to(0.1, { position:v3(tx,this.node.position.y,0)}).start() } //英雄不再后退 // if(this.model.fac==FacSet.HERO) { // let tx=this.node.position.x-30 // if(tx < -320) tx=-320 // tween(this.node).to(0.1, { position:v3(tx,this.node.position.y,0)}).start() // } } // 伤害计算和战斗逻辑已迁移到 HeroBattleSystem /** 死亡触发器(预留,用于天赋/特殊效果) */ do_dead_trigger(){ if(!this.model || this.model.is_dead || this.model.fac === FacSet.MON) return; // 预留:天赋触发、复活检查等 } /** 受击触发器(预留,用于天赋/特殊效果) */ do_atked_trigger(){ if(!this.model || this.model.is_dead || this.model.fac === FacSet.MON) return; // 预留:反击、护盾触发等 } /** 调试日志(已禁用) */ to_console(value:any, value2:any=null, value3:any=null){ // 调试用,生产环境已禁用 } reset() { 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.tooltip(TooltipTypes.skill, skill.name, skill_id) break case "atk": this.as.atk() break } } /** 显示伤害数字 */ showDamage(damage: number, isCrit: boolean,DAnm:number) { let anm=EAnmConf[DAnm].path // DAnm和EAnm共用设定数组 this.damageQueue.push({ damage, isCrit, delay: this.damageInterval, anm }); } /** 处理伤害队列 */ private processDamageQueue() { if (this.isProcessingDamage || this.damageQueue.length === 0) return; this.isProcessingDamage = true; const damageInfo = this.damageQueue.shift()!; this.showDamageImmediate(damageInfo.damage, damageInfo.isCrit,damageInfo.anm); // 设置延时处理下一个伤 this.scheduleOnce(() => { this.isProcessingDamage = false; }, this.damageInterval); } /** 立即显示伤害效果 */ private showDamageImmediate(damage: number, isCrit: boolean, anm:string="atked") { if (!this.model) return; this.hp_show(this.model.hp, this.model.Attrs[Attrs.HP_MAX]); this.in_atked(anm, this.model.fac==FacSet.HERO?1:-1); if (isCrit) { this.hp_tip(TooltipTypes.crit, damage.toFixed(0), damage); } else { this.hp_tip(TooltipTypes.life, damage.toFixed(0), damage); } } }