feat: 新增卡牌长按预览功能与英雄信息等级展示
1. 为所有卡牌组件添加长按0.5秒弹出详情面板功能,支持点击/取消取消计时 2. 为英雄配置添加分级能力说明数组,信息面板支持逐行展示各等级效果 3. 重构英雄信息面板UI布局与渲染逻辑,优化动画加载方式 4. 更新卡牌预制体尺寸与样式,适配新的交互与展示需求
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -172,6 +172,8 @@ export interface heroInfo {
|
||||
skills: Record<number, HSkillInfo>; // 携带技能ID列表
|
||||
evolve?: Record<number, HeroEvolve>; // 等级进化配置,key=等级(2,3,...)
|
||||
info: string; // 描述文案
|
||||
/** 分级能力说明,索引0=1级,依次递增;信息面板逐条展示各等级技能与能力 */
|
||||
infos?: string[];
|
||||
}
|
||||
/**
|
||||
* 技能基础信息接口
|
||||
@@ -239,7 +241,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每受击3次为自身添加护盾(层数随等级4→5→6);6级起每受击5次回复攻击力250%生命"
|
||||
info: "每受击3次为自身添加护盾(层数随等级4→5→6);6级起每受击5次回复攻击力250%生命",
|
||||
infos: [
|
||||
"受击3次获得4次格挡护盾",
|
||||
"受击3次获得5次格挡护盾",
|
||||
"额外+150最大生命",
|
||||
"受击3次获得6次格挡护盾",
|
||||
"额外+150最大生命,+10攻击力",
|
||||
"受击3次获得6次格挡护盾;受击5次回复攻击力250%生命",
|
||||
]
|
||||
},
|
||||
5012: {
|
||||
uuid: 5012, name: "不死小强", path: "hk2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Melee, hp: 600, ap: 57,
|
||||
@@ -257,7 +267,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每受击3次回复自身攻击力250%生命;2级治疗量300%,4级350%,5级触发阈值降至2次;6级起每受击5次为自身添加4层护盾"
|
||||
info: "每受击3次回复自身攻击力250%生命;2级治疗量300%,4级350%,5级触发阈值降至2次;6级起每受击5次为自身添加4层护盾",
|
||||
infos: [
|
||||
"受击3次回复攻击力250%生命",
|
||||
"受击3次回复攻击力300%生命",
|
||||
"额外+150最大生命",
|
||||
"受击3次回复攻击力350%生命",
|
||||
"受击2次回复攻击力350%生命",
|
||||
"受击2次回复攻击力350%生命;受击5次获得4次格挡护盾",
|
||||
]
|
||||
},
|
||||
// 以下英雄暂时注释,保留待后续扩展
|
||||
// 5012:{uuid:5012,name:"不死小强",path:"hk2", fac:FacSet.HERO,pool_lv:2,lv:1,type:HType.Melee,hp:600,ap:57,
|
||||
@@ -306,7 +324,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每攻击5次永久提升自身攻击力(8→10→12,5级起每4次);6级起每攻击7次永久+20%暴击伤害"
|
||||
info: "每攻击5次永久提升自身攻击力(8→10→12,5级起每4次);6级起每攻击7次永久+20%暴击伤害",
|
||||
infos: [
|
||||
"攻击5次永久+8攻击力",
|
||||
"攻击5次永久+10攻击力",
|
||||
"额外+80最大生命,+20攻击力",
|
||||
"攻击5次永久+12攻击力",
|
||||
"攻击4次永久+12攻击力",
|
||||
"攻击4次永久+12攻击力;攻击7次永久+20%暴击伤害",
|
||||
]
|
||||
},
|
||||
5022: {
|
||||
uuid: 5022, name: "嗜血剑客", path: "hc2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Melee, hp: 900, ap: 85,
|
||||
@@ -337,7 +363,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每攻击5次自身获得+15%暴击率(持续5秒),每攻击7次永久+12攻击力;2级暴击+20%,4级持续6秒且攻击+15,5级暴击触发阈值降至4次;6级起每攻击7次永久+15%暴伤"
|
||||
info: "每攻击5次自身获得+15%暴击率(持续5秒),每攻击7次永久+12攻击力;2级暴击+20%,4级持续6秒且攻击+15,5级暴击触发阈值降至4次;6级起每攻击7次永久+15%暴伤",
|
||||
infos: [
|
||||
"攻击5次获得+15%暴击率5秒;攻击7次永久+12攻击力",
|
||||
"攻击5次获得+20%暴击率5秒;攻击7次永久+12攻击力",
|
||||
"额外+80最大生命,+20攻击力",
|
||||
"攻击5次获得+20%暴击率6秒;攻击7次永久+15攻击力",
|
||||
"攻击4次获得+20%暴击率6秒;攻击7次永久+15攻击力",
|
||||
"攻击4次获得+20%暴击率6秒;攻击7次永久+15攻击力;攻击7次永久+15%暴击伤害",
|
||||
]
|
||||
},
|
||||
// 5022: {
|
||||
// uuid: 5022, name: "嗜血剑客", path: "hc2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Melee, hp: 900, ap: 85,
|
||||
@@ -372,7 +406,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
5: { atking: [{ s_uuid: 6401, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 2, ap: 12 } }] },
|
||||
6: { field: [7006] },
|
||||
},
|
||||
info: "每攻击5次为随机队友永久提升攻击力(8→10→12,5级起2名);6级起全队攻击速度+10%"
|
||||
info: "每攻击5次为随机队友永久提升攻击力(8→10→12,5级起2名);6级起全队攻击速度+10%",
|
||||
infos: [
|
||||
"攻击5次为随机1名队友永久+8攻击力",
|
||||
"攻击5次为随机1名队友永久+10攻击力",
|
||||
"额外+60最大生命",
|
||||
"攻击5次为随机1名队友永久+12攻击力",
|
||||
"攻击5次为随机2名队友永久+12攻击力",
|
||||
"攻击5次为随机2名队友永久+12攻击力;驻场光环:全队攻击速度+10%",
|
||||
]
|
||||
},
|
||||
5032: {
|
||||
uuid: 5032, name: "战术弓手", path: "ha2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120,
|
||||
@@ -390,7 +432,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每攻击5次为随机3名队友提供+10%暴击率(持续5秒);2级+12%,4级目标数4名且持续6秒,5级触发阈值降至4次;6级起每攻击7次为随机3名队友永久+12%暴伤"
|
||||
info: "每攻击5次为随机3名队友提供+10%暴击率(持续5秒);2级+12%,4级目标数4名且持续6秒,5级触发阈值降至4次;6级起每攻击7次为随机3名队友永久+12%暴伤",
|
||||
infos: [
|
||||
"攻击5次为随机3名队友+10%暴击率5秒",
|
||||
"攻击5次为随机3名队友+12%暴击率5秒",
|
||||
"额外+80最大生命",
|
||||
"攻击5次为随机4名队友+12%暴击率6秒",
|
||||
"攻击4次为随机4名队友+12%暴击率6秒",
|
||||
"攻击4次为随机4名队友+12%暴击率6秒;攻击7次为随机3名队友永久+12%暴击伤害",
|
||||
]
|
||||
},
|
||||
// 5032: {
|
||||
// uuid: 5032, name: "战术弓手", path: "ha2", fac: FacSet.HERO, pool_lv: 3, lv: 1, type: HType.Long, hp: 430, ap: 120,
|
||||
@@ -468,7 +518,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 6级终极遗志:追加 AP+30;终极涅槃:复活 2 次血量 70%
|
||||
6: { dead: [{ s_uuid: 6301, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 8 } }, { s_uuid: 6402, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 150 } }, { s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 30 } }], revive: { s_uuid: 6501, r_num: 2, upr: 0.7 } },
|
||||
},
|
||||
info: "死亡时为全队提供 80 永久生命值与 5 层护盾;6级解锁 30 永久攻击力,且可复活 2 次"
|
||||
info: "死亡时为全队提供 80 永久生命值与 5 层护盾;6级解锁 30 永久攻击力,且可复活 2 次",
|
||||
infos: [
|
||||
"死亡时全队永久+80最大生命、获得5次格挡护盾;可复活1次(50%血量)",
|
||||
"死亡时全队永久+100最大生命、获得6次格挡护盾;可复活1次(50%血量)",
|
||||
"死亡时全队永久+100最大生命、获得6次格挡护盾;可复活1次(60%血量)",
|
||||
"死亡时全队永久+120最大生命、获得7次格挡护盾;可复活1次(60%血量)",
|
||||
"死亡时全队永久+150最大生命、获得8次格挡护盾;可复活1次(60%血量)",
|
||||
"死亡时全队永久+150最大生命、+30攻击力、获得8次格挡护盾;可复活2次(70%血量)",
|
||||
]
|
||||
},
|
||||
|
||||
// ========== fstart 类(法师 · 战前增益) ==========
|
||||
@@ -485,7 +543,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
5: { fstart: [{ s_uuid: 6401, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7111, buff_value: 90, buff_duration: 10 } }] },
|
||||
6: { field: [7204] },
|
||||
},
|
||||
info: "战斗开始时全队攻击力限时提升(+30/5s→+60/7s→+90/10s);6级起全队暴击率+20%(驻场)"
|
||||
info: "战斗开始时全队攻击力限时提升(+30/5s→+60/7s→+90/10s);6级起全队暴击率+20%(驻场)",
|
||||
infos: [
|
||||
"战斗开始全队攻击力+30,持续5秒",
|
||||
"战斗开始全队攻击力+30,持续5秒;额外+80最大生命",
|
||||
"战斗开始全队攻击力+60,持续7秒",
|
||||
"战斗开始全队攻击力+60,持续7秒;额外+80最大生命",
|
||||
"战斗开始全队攻击力+90,持续10秒",
|
||||
"战斗开始全队攻击力+90,持续10秒;驻场光环:全队暴击率+20%",
|
||||
]
|
||||
},
|
||||
5052: {
|
||||
uuid: 5052, name: "护盾牧师", path: "hm2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80,
|
||||
@@ -498,7 +564,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
5: { fstart: [{ s_uuid: 6301, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 6 } }] },
|
||||
6: { field: [7007] },
|
||||
},
|
||||
info: "战斗开始时为全队添加3层护盾;2级4层,4级5层,5级6层;6级起全队最大生命值+10%(驻场)"
|
||||
info: "战斗开始时为全队添加3层护盾;2级4层,4级5层,5级6层;6级起全队最大生命值+10%(驻场)",
|
||||
infos: [
|
||||
"战斗开始全队获得3次格挡护盾",
|
||||
"战斗开始全队获得4次格挡护盾",
|
||||
"战斗开始全队获得4次格挡护盾;额外+80最大生命",
|
||||
"战斗开始全队获得5次格挡护盾",
|
||||
"战斗开始全队获得6次格挡护盾",
|
||||
"战斗开始全队获得6次格挡护盾;驻场光环:全队最大生命+10%",
|
||||
]
|
||||
},
|
||||
// 5052: {
|
||||
// uuid: 5052, name: "护盾牧师", path: "hm2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80,
|
||||
@@ -556,7 +630,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
5: { field: [7202, 7206] },
|
||||
6: { field: [7202, 7206, 7007] },
|
||||
},
|
||||
info: "驻场期间全队攻击力+10%;2级+20%,4级追加攻击速度+10%,5级攻速+20%;6级追加最大生命值+10%"
|
||||
info: "驻场期间全队攻击力+10%;2级+20%,4级追加攻击速度+10%,5级攻速+20%;6级追加最大生命值+10%",
|
||||
infos: [
|
||||
"驻场光环:全队攻击力+10%",
|
||||
"驻场光环:全队攻击力+20%",
|
||||
"驻场光环:全队攻击力+20%;额外+80最大生命",
|
||||
"驻场光环:全队攻击力+20%、攻击速度+10%",
|
||||
"驻场光环:全队攻击力+20%、攻击速度+20%",
|
||||
"驻场光环:全队攻击力+20%、攻击速度+20%、最大生命+10%",
|
||||
]
|
||||
},
|
||||
|
||||
// ========== fend + atking 类(辅助 · 治疗续航 + 波次增益) ==========
|
||||
@@ -578,7 +660,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
]
|
||||
},
|
||||
},
|
||||
info: "每攻击5次治疗最缺血队友(200→300%,5级起全队);6级起每攻击7次为全队添加3层护盾"
|
||||
info: "每攻击5次治疗最缺血队友(200→300%,5级起全队);6级起每攻击7次为全队添加3层护盾",
|
||||
infos: [
|
||||
"攻击5次治疗最缺血队友,回复攻击力200%生命",
|
||||
"攻击5次治疗最缺血队友,回复攻击力250%生命",
|
||||
"攻击5次治疗最缺血队友,回复攻击力250%生命;额外+80最大生命",
|
||||
"攻击5次治疗最缺血队友,回复攻击力300%生命",
|
||||
"攻击5次治疗2名最缺血队友,回复攻击力300%生命",
|
||||
"攻击5次治疗2名最缺血队友,回复攻击力300%生命;攻击7次全队获得3次格挡护盾",
|
||||
]
|
||||
},
|
||||
// 5072: {
|
||||
// uuid: 5072, name: "小金库", path: "hh2", fac: FacSet.HERO, pool_lv: 2, lv: 1, type: HType.Long, hp: 287, ap: 80,
|
||||
|
||||
@@ -84,18 +84,45 @@ export class HeroViewComp extends CCComp {
|
||||
// let anm = this.node.getChildByName("anm")
|
||||
// anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8);
|
||||
|
||||
// 绑定点击事件,点击打开英雄信息面板弹窗
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onHeroClicked, this);
|
||||
// 绑定长按事件,长按打开英雄信息面板,放开关闭
|
||||
this.node.on(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
}
|
||||
|
||||
private onHeroClicked() {
|
||||
/** 长按时长(秒) */
|
||||
private static readonly LONG_PRESS_TIME: number = 0.5;
|
||||
/** 信息框是否已弹出(区分长按完成与短按取消) */
|
||||
private info_shown: boolean = false;
|
||||
|
||||
/** 按下:启动长按计时(仅英雄阵营响应) */
|
||||
private onInfoTouchStart() {
|
||||
if (!this.model) return;
|
||||
if (this.model.fac !== FacSet.HERO) return;
|
||||
oops.audio.playEffect("music/button");
|
||||
this.info_shown = false;
|
||||
this.scheduleOnce(this.showInfo, HeroViewComp.LONG_PRESS_TIME);
|
||||
}
|
||||
|
||||
/** 放开/取消:已弹出则关闭信息框,未弹出则取消长按计时 */
|
||||
private onInfoTouchEnd() {
|
||||
this.unschedule(this.showInfo);
|
||||
if (this.info_shown) {
|
||||
this.info_shown = false;
|
||||
// oops.gui 移除信息框
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/** 长按到点:弹出英雄信息面板(实体数据模式,随战场属性实时刷新) */
|
||||
private showInfo() {
|
||||
if (!this.model) return;
|
||||
if (this.model.fac !== FacSet.HERO) return;
|
||||
const eid = this.ent?.eid;
|
||||
if (!eid) return;
|
||||
|
||||
this.info_shown = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
// oops.gui 打开 HInfo 英雄信息面板
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, { eid: eid });
|
||||
}
|
||||
@@ -679,8 +706,10 @@ export class HeroViewComp extends CCComp {
|
||||
this.damageQueue.length = 0;
|
||||
this.isProcessingDamage = false;
|
||||
|
||||
// 解绑点击事件
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onHeroClicked, this);
|
||||
// 解绑长按事件
|
||||
this.node.off(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
|
||||
// 节点生命周期由 Monster 对象池管理,此处不再销毁
|
||||
// if (this.node && this.node.isValid) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { HeroInfo } from "../common/config/heroSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { FieldSkillType } from "../common/config/SkillSet";
|
||||
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
||||
@@ -112,6 +113,11 @@ export class CHeroComp extends CCComp {
|
||||
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
|
||||
// 卡面长按弹信息框:按下启动计时,放开/取消时关闭
|
||||
this.node.on(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
|
||||
// 缓存透明度组件(用于淡入淡出动画)
|
||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
|
||||
@@ -129,6 +135,13 @@ export class CHeroComp extends CCComp {
|
||||
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node.off(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
}
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.off(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
}
|
||||
// 清理长按计时器,防止销毁后回调访问失效节点
|
||||
this.unschedule(this.showInfo);
|
||||
}
|
||||
|
||||
// ======================== 数据初始化 ========================
|
||||
@@ -459,6 +472,51 @@ export class CHeroComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按信息框 ========================
|
||||
|
||||
/** 长按时长(秒) */
|
||||
private static readonly LONG_PRESS_TIME: number = 0.5;
|
||||
/** 信息框是否已弹出(区分长按完成与短按取消) */
|
||||
private info_shown: boolean = false;
|
||||
|
||||
/** 卡面按下:启动长按计时 */
|
||||
private onInfoTouchStart() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = false;
|
||||
this.scheduleOnce(this.showInfo, CHeroComp.LONG_PRESS_TIME);
|
||||
}
|
||||
|
||||
/** 卡面放开/取消:已弹出则关闭信息框,未弹出则取消长按计时 */
|
||||
private onInfoTouchEnd() {
|
||||
this.unschedule(this.showInfo);
|
||||
if (this.info_shown) {
|
||||
this.info_shown = false;
|
||||
// oops.gui 移除信息框
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按到点:弹出英雄信息框。
|
||||
* 升级卡(SpecialUpgrade + target_hero_eid)通过 eid 反查场上实体得到 hero_uuid,
|
||||
* 与 updateUI 的渲染逻辑保持一致。
|
||||
*/
|
||||
private showInfo() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = true;
|
||||
|
||||
const isUpgradeCard = this.cardData.type === CardType.SpecialUpgrade && !!this.cardData.target_hero_eid;
|
||||
const heroUuid = isUpgradeCard
|
||||
? (this.queryHeroUuidByEid(this.cardData.target_hero_eid as number) ?? this.cardData.uuid)
|
||||
: this.cardData.uuid;
|
||||
const heroLv = Math.max(1, this.cardData.hero_lv ?? 1);
|
||||
const poolLv = Math.max(1, this.cardData.base_pool_lv ?? this.cardData.pool_lv ?? 1);
|
||||
|
||||
// oops.gui 打开 HInfo 英雄卡预览
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, { heroUuid, heroLv, poolLv });
|
||||
}
|
||||
|
||||
// ======================== 购买逻辑 ========================
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ import { FieldSkillSet } from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -87,6 +88,10 @@ export class EquipListComp extends CCComp {
|
||||
this.buy_node?.on(NodeEventType.TOUCH_START, this.onBuyTouchStart, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
// 卡面长按弹信息框:按下启动计时,放开/取消时关闭
|
||||
this.node.on(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
}
|
||||
|
||||
@@ -97,6 +102,13 @@ export class EquipListComp extends CCComp {
|
||||
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node.off(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
}
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.off(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
}
|
||||
// 清理长按计时器,防止销毁后回调访问失效节点
|
||||
this.unschedule(this.showInfo);
|
||||
}
|
||||
|
||||
// ======================== 数据初始化 ========================
|
||||
@@ -298,6 +310,45 @@ export class EquipListComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按信息框 ========================
|
||||
|
||||
/** 长按时长(秒) */
|
||||
private static readonly LONG_PRESS_TIME: number = 0.5;
|
||||
/** 信息框是否已弹出(区分长按完成与短按取消) */
|
||||
private info_shown: boolean = false;
|
||||
|
||||
/** 卡面按下:启动长按计时 */
|
||||
private onInfoTouchStart() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = false;
|
||||
this.scheduleOnce(this.showInfo, EquipListComp.LONG_PRESS_TIME);
|
||||
}
|
||||
|
||||
/** 卡面放开/取消:已弹出则关闭信息框,未弹出则取消长按计时 */
|
||||
private onInfoTouchEnd() {
|
||||
this.unschedule(this.showInfo);
|
||||
if (this.info_shown) {
|
||||
this.info_shown = false;
|
||||
// oops.gui 移除信息框
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/** 长按到点:弹出装备信息框 */
|
||||
private showInfo() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = true;
|
||||
// oops.gui 打开 HInfo 装备卡预览(复用 EquipBoxComp 的打开参数)
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, {
|
||||
heroUuid: this.cardData.uuid,
|
||||
heroLv: this.cardData.card_lv ?? 1,
|
||||
poolLv: 1,
|
||||
isSkillCard: true,
|
||||
isEquipCard: true
|
||||
});
|
||||
}
|
||||
|
||||
// ======================== 购买逻辑 ========================
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
* - Hero —— 英雄 ECS 实体类(用于出售删除)
|
||||
* - UIID.IBox —— 英雄详情弹窗 ID
|
||||
*/
|
||||
import { _decorator, Animation, AnimationClip, Button, Event, EventTouch, Input, Label, Node, NodeEventType, Sprite, UITransform, input, resources, CCInteger, SpriteFrame } from "cc";
|
||||
import { _decorator, AnimationClip, Button, Event, EventTouch, Input, Label, Layout, Node, NodeEventType, Sprite, UITransform, input, resources, CCInteger, SpriteFrame } 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 { HeroInfo } from "../common/config/heroSet";
|
||||
import { heroInfo, HeroInfo } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { Hero } from "../hero/Hero";
|
||||
@@ -108,6 +108,8 @@ export class HInfoComp extends CCComp {
|
||||
private nameLabel: Label | null = null;
|
||||
/** 技能信息标签缓存引用 */
|
||||
private infoLabel: Label | null = null;
|
||||
/** infos 逐条展示的 Label 池(索引0复用预制体 Label,其余动态创建) */
|
||||
private infoLineLabels: Label[] = [];
|
||||
/** AP 标签缓存引用 */
|
||||
private apLabel: Label | null = null;
|
||||
/** AP 加成标签缓存引用 */
|
||||
@@ -202,7 +204,7 @@ export class HInfoComp extends CCComp {
|
||||
if (this.hpPlusLabel) this.hpPlusLabel.node.active = false;
|
||||
}
|
||||
if (this.infoLabel) {
|
||||
this.infoLabel.string = config.info ?? "";
|
||||
this.renderFallbackLines(config.info ?? "");
|
||||
}
|
||||
if (this.cdLabel) {
|
||||
this.cdLabel.string = config.t_inv ? `${config.t_inv}s` : "0s";
|
||||
@@ -243,7 +245,7 @@ export class HInfoComp extends CCComp {
|
||||
}
|
||||
|
||||
if (this.infoLabel) {
|
||||
this.infoLabel.string = buildSkillDesc(hero);
|
||||
this.renderHeroInfoLines(hero);
|
||||
}
|
||||
|
||||
if (this.cdLabel) {
|
||||
@@ -351,7 +353,11 @@ export class HInfoComp extends CCComp {
|
||||
// ---- 技能信息标签 ----
|
||||
if (this.infoLabel) {
|
||||
const heroData = HeroInfo[heroUuid];
|
||||
this.infoLabel.string = heroData ? buildSkillDesc(heroData) : "";
|
||||
if (heroData) {
|
||||
this.renderHeroInfoLines(heroData);
|
||||
} else {
|
||||
this.renderFallbackLines("");
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 数值标签 ----
|
||||
@@ -405,6 +411,61 @@ export class HInfoComp extends CCComp {
|
||||
return !!(this.model as any)?.ent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 逐条展示英雄能力说明:infos 每项一行(自动加 Lv 前缀,Lv 着色)。
|
||||
* 复用预制体首条 Label,其余按需创建/隐藏,避免频繁增删节点。
|
||||
* 未配置 infos 时(如怪物)回退为单行 buildSkillDesc 描述。
|
||||
* @param hero 英雄静态配置
|
||||
*/
|
||||
private renderHeroInfoLines(hero: heroInfo) {
|
||||
if (!this.infoLabel) return;
|
||||
const hasInfos = !!(hero.infos?.length);
|
||||
const count = hasInfos ? hero.infos!.length : 1;
|
||||
|
||||
// 按需扩容 Label 池(克隆首条保持样式一致)
|
||||
for (let i = this.infoLineLabels.length; i < count; i++) {
|
||||
const node = new Node(`info_${i}`);
|
||||
node.layer = this.info_node.layer;
|
||||
this.info_node.addChild(node);
|
||||
const label = node.addComponent(Label);
|
||||
label.fontSize = this.infoLabel.fontSize;
|
||||
label.lineHeight = this.infoLabel.lineHeight;
|
||||
label.horizontalAlign = Label.HorizontalAlign.LEFT;
|
||||
label.verticalAlign = Label.VerticalAlign.TOP;
|
||||
label.color = this.infoLabel.color;
|
||||
this.infoLineLabels.push(label);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.infoLineLabels.length; i++) {
|
||||
const label = this.infoLineLabels[i];
|
||||
if (i < count) {
|
||||
label.node.active = true;
|
||||
if (hasInfos) {
|
||||
label.string = `Lv${i + 1}. ${hero.infos![i]}`;
|
||||
// 无富文本时用白色突出 Lv 前缀外的正文,保持简洁
|
||||
label.color = this.infoLabel.color;
|
||||
} else {
|
||||
label.string = buildSkillDesc(hero);
|
||||
}
|
||||
} else {
|
||||
label.node.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单行文本展示(技能卡/无配置英雄):隐藏逐条 Label 池,只保留首行。
|
||||
* @param text 展示文本
|
||||
*/
|
||||
private renderFallbackLines(text: string) {
|
||||
if (!this.infoLabel) return;
|
||||
this.infoLabel.node.active = true;
|
||||
this.infoLabel.string = text;
|
||||
for (let i = 1; i < this.infoLineLabels.length; i++) {
|
||||
this.infoLineLabels[i].node.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 内部工具方法 ========================
|
||||
|
||||
/** 缓存 AP / HP Label 引用,避免每次刷新都遍历节点树 */
|
||||
@@ -415,14 +476,22 @@ export class HInfoComp extends CCComp {
|
||||
if (!this.infoLabel && this.info_node) {
|
||||
this.infoLabel = this.info_node.getComponentInChildren(Label);
|
||||
if (!this.infoLabel) {
|
||||
const child = this.info_node.children[0] || this.info_node;
|
||||
this.infoLabel = child.getComponent(Label) || child.addComponent(Label);
|
||||
const child = new Node("info_0");
|
||||
child.layer = this.info_node.layer;
|
||||
this.info_node.addChild(child);
|
||||
this.infoLabel = child.addComponent(Label);
|
||||
this.infoLabel.fontSize = 20;
|
||||
this.infoLabel.lineHeight = 28;
|
||||
this.infoLabel.overflow = Label.Overflow.SHRINK;
|
||||
this.infoLabel.horizontalAlign = Label.HorizontalAlign.LEFT;
|
||||
this.infoLabel.verticalAlign = Label.VerticalAlign.TOP;
|
||||
}
|
||||
// 垂直 Layout 让多条 Label 自动逐行排布
|
||||
let layout = this.info_node.getComponent(Layout);
|
||||
if (!layout) layout = this.info_node.addComponent(Layout);
|
||||
layout.type = Layout.Type.VERTICAL;
|
||||
layout.resizeMode = Layout.ResizeMode.CONTAINER;
|
||||
layout.spacingY = 4;
|
||||
this.infoLineLabels = [this.infoLabel];
|
||||
}
|
||||
if (!this.apLabel && this.ap_node) {
|
||||
this.apLabel = this.ap_node.getChildByName("val")?.getComponent(Label) || null;
|
||||
@@ -454,7 +523,8 @@ export class HInfoComp extends CCComp {
|
||||
// ======================== 英雄动画 ========================
|
||||
|
||||
/**
|
||||
* 为英雄图标加载并播放 idle 动画(带竞态保护)。
|
||||
* 为英雄图标加载 idle 动画剪辑,提取首帧 SpriteFrame 静止展示。
|
||||
* 不经 Animation 组件采样,零运行时开销;帧名各异也无需关心。
|
||||
* @param node 图标节点
|
||||
* @param uuid 英雄 UUID
|
||||
* @param token 视觉令牌
|
||||
@@ -464,12 +534,7 @@ export class HInfoComp extends CCComp {
|
||||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
if (sprite) sprite.spriteFrame = null;
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) {
|
||||
this.clearIconAnimation(node);
|
||||
return;
|
||||
}
|
||||
const anim = node.getComponent(Animation) || node.addComponent(Animation);
|
||||
this.clearAnimationClips(anim);
|
||||
if (!hero) return;
|
||||
const path = `game/heros/hero/${hero.path}/idle`;
|
||||
resources.load(path, AnimationClip, (err, clip) => {
|
||||
if (err || !clip) return;
|
||||
@@ -479,9 +544,24 @@ export class HInfoComp extends CCComp {
|
||||
} else {
|
||||
if (!this.model || this.model.hero_uuid !== uuid) return;
|
||||
}
|
||||
this.clearAnimationClips(anim);
|
||||
anim.addClip(clip);
|
||||
anim.play("idle");
|
||||
// 异步加载期间面板可能已关闭销毁
|
||||
if (!node || !node.isValid) return;
|
||||
const target = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
if (!target || !target.isValid) return;
|
||||
// 帧动画数据存于 spriteFrame 轨道的 _channel._curve._values(裸 SpriteFrame 数组)。
|
||||
// 注:_values 为引擎私有字段,但本版本公开 values getter 反序列化后为空,只能读私有字段取首帧。
|
||||
let firstFrame: SpriteFrame | undefined;
|
||||
for (const track of clip.tracks) {
|
||||
const curve = (track as unknown as { channel?: { curve?: { _values?: unknown[] } } }).channel?.curve;
|
||||
const frame = curve?._values?.[0];
|
||||
if (frame instanceof SpriteFrame) {
|
||||
firstFrame = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstFrame) {
|
||||
target.spriteFrame = firstFrame;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -494,7 +574,6 @@ export class HInfoComp extends CCComp {
|
||||
*/
|
||||
private updateSkillAnimation(node: Node, config: CardConfig, token: number) {
|
||||
if (!node) return;
|
||||
this.clearIconAnimation(node);
|
||||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
if (!sprite) return;
|
||||
sprite.spriteFrame = null;
|
||||
@@ -520,21 +599,6 @@ export class HInfoComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
/** 停止并清除图标节点上的动画 */
|
||||
private clearIconAnimation(node: Node) {
|
||||
const anim = node?.getComponent(Animation);
|
||||
if (!anim) return;
|
||||
anim.stop();
|
||||
this.clearAnimationClips(anim);
|
||||
}
|
||||
|
||||
/** 移除 Animation 组件上的全部 AnimationClip */
|
||||
private clearAnimationClips(anim: Animation) {
|
||||
const clips = (anim as any).clips as AnimationClip[] | undefined;
|
||||
if (!clips || clips.length === 0) return;
|
||||
[...clips].forEach(clip => anim.removeClip(clip, true));
|
||||
}
|
||||
|
||||
// ======================== 交互 ========================
|
||||
|
||||
private bindEvents() {
|
||||
@@ -637,9 +701,8 @@ export class HInfoComp extends CCComp {
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
|
||||
/** ECS 组件移除时的释放钩子:清理动画资源并销毁节点 */
|
||||
/** ECS 组件移除时的释放钩子:重置绑定状态 */
|
||||
reset() {
|
||||
this.clearIconAnimation(this.icon_node);
|
||||
this.iconVisualToken = 0;
|
||||
this.iconHeroUuid = 0;
|
||||
this.model = null;
|
||||
|
||||
@@ -19,10 +19,12 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { CardConfig, CardTriggerType, CKind } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { SkillCardList } from "../common/config/SCardSet";
|
||||
import { FieldSkillSet, SkillSet } from "../common/config/SkillSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -89,6 +91,10 @@ export class ItemListComp extends CCComp {
|
||||
this.buy_node?.on(NodeEventType.TOUCH_START, this.onBuyTouchStart, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
// 卡面长按弹信息框:按下启动计时,放开/取消时关闭
|
||||
this.node.on(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
}
|
||||
|
||||
@@ -99,6 +105,13 @@ export class ItemListComp extends CCComp {
|
||||
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node.off(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
}
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.off(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
}
|
||||
// 清理长按计时器,防止销毁后回调访问失效节点
|
||||
this.unschedule(this.showInfo);
|
||||
}
|
||||
|
||||
// ======================== 数据初始化 ========================
|
||||
@@ -344,6 +357,65 @@ export class ItemListComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按信息框 ========================
|
||||
|
||||
/** 长按时长(秒) */
|
||||
private static readonly LONG_PRESS_TIME: number = 0.5;
|
||||
/** 信息框是否已弹出(区分长按完成与短按取消) */
|
||||
private info_shown: boolean = false;
|
||||
|
||||
/** 卡面按下:启动长按计时 */
|
||||
private onInfoTouchStart() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = false;
|
||||
this.scheduleOnce(this.showInfo, ItemListComp.LONG_PRESS_TIME);
|
||||
}
|
||||
|
||||
/** 卡面放开/取消:已弹出则关闭信息框,未弹出则取消长按计时 */
|
||||
private onInfoTouchEnd() {
|
||||
this.unschedule(this.showInfo);
|
||||
if (this.info_shown) {
|
||||
this.info_shown = false;
|
||||
// oops.gui 移除信息框
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按到点:按卡牌类型弹出对应信息框。
|
||||
* - 装备卡(trigger_type=Field)→ 装备预览(isEquipCard)
|
||||
* - 商品/药品及其他 → 技能卡预览(uuid 反查 SkillCardList)
|
||||
*/
|
||||
private showInfo() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = true;
|
||||
|
||||
const isEquip = this.cardData.trigger_type === CardTriggerType.Field;
|
||||
if (isEquip) {
|
||||
// oops.gui 打开 HInfo 装备卡预览(复用 EquipBoxComp 的打开参数)
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, {
|
||||
heroUuid: this.cardData.uuid,
|
||||
heroLv: this.cardData.card_lv ?? 1,
|
||||
poolLv: 1,
|
||||
isSkillCard: true,
|
||||
isEquipCard: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 技能/商品卡:反查 SkillCardList 拿到卡池 uuid(查不到时降级为本卡 uuid)
|
||||
const config = SkillCardList.find(c => c.uuid === this.cardData!.uuid || c.skill === this.cardData!.uuid);
|
||||
const cardUuid = config ? config.uuid : this.cardData.uuid;
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, {
|
||||
heroUuid: cardUuid,
|
||||
heroLv: this.cardData.card_lv ?? 1,
|
||||
poolLv: config?.pool_lv ?? 1,
|
||||
isSkillCard: true
|
||||
});
|
||||
}
|
||||
|
||||
// ======================== 购买逻辑 ========================
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import { FieldSkillSet, SkillSet, SkillConfig } from "../common/config/SkillSet"
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -86,6 +87,10 @@ export class SCardComp extends CCComp {
|
||||
this.buy_node?.on(NodeEventType.TOUCH_START, this.onBuyTouchStart, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node?.on(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
// 卡面长按弹信息框:按下启动计时,放开/取消时关闭
|
||||
this.node.on(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.on(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
}
|
||||
|
||||
@@ -96,6 +101,13 @@ export class SCardComp extends CCComp {
|
||||
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this);
|
||||
this.buy_node.off(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this);
|
||||
}
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.off(NodeEventType.TOUCH_START, this.onInfoTouchStart, this);
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onInfoTouchEnd, this);
|
||||
this.node.off(NodeEventType.TOUCH_CANCEL, this.onInfoTouchEnd, this);
|
||||
}
|
||||
// 清理长按计时器,防止销毁后回调访问失效节点
|
||||
this.unschedule(this.showInfo);
|
||||
}
|
||||
|
||||
// ======================== 数据初始化 ========================
|
||||
@@ -355,6 +367,46 @@ export class SCardComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按信息框 ========================
|
||||
|
||||
/** 长按时长(秒) */
|
||||
private static readonly LONG_PRESS_TIME: number = 0.5;
|
||||
/** 信息框是否已弹出(区分长按完成与短按取消) */
|
||||
private info_shown: boolean = false;
|
||||
|
||||
/** 卡面按下:启动长按计时 */
|
||||
private onInfoTouchStart() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = false;
|
||||
this.scheduleOnce(this.showInfo, SCardComp.LONG_PRESS_TIME);
|
||||
}
|
||||
|
||||
/** 卡面放开/取消:已弹出则关闭信息框,未弹出则取消长按计时 */
|
||||
private onInfoTouchEnd() {
|
||||
this.unschedule(this.showInfo);
|
||||
if (this.info_shown) {
|
||||
this.info_shown = false;
|
||||
// oops.gui 移除信息框
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/** 长按到点:弹出技能卡信息框 */
|
||||
private showInfo() {
|
||||
if (!this.cardData) return;
|
||||
this.info_shown = true;
|
||||
// oops.gui 打开 HInfo 技能卡预览(复用 SkillBoxComp 的查询逻辑)
|
||||
const config = SkillCardList.find(c => c.uuid === this.cardData!.uuid || c.skill === this.cardData!.uuid);
|
||||
const cardUuid = config ? config.uuid : this.cardData.uuid;
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, {
|
||||
heroUuid: cardUuid,
|
||||
heroLv: this.cardData.card_lv ?? 1,
|
||||
poolLv: config?.pool_lv ?? 1,
|
||||
isSkillCard: true
|
||||
});
|
||||
}
|
||||
|
||||
// ======================== 购买逻辑 ========================
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user