feat(关卡): 添加英雄数量上限机制
- 在 MissionCardComp 中添加英雄数量显示与上限控制逻辑 - 当英雄数量达到上限时禁止使用英雄卡牌 - 英雄死亡时减少当前英雄计数并刷新显示 - 添加英雄数量变化的动画反馈效果 - 移除 SingletonModuleComp 中未使用的 unlockCoin 字段
This commit is contained in:
@@ -6,6 +6,8 @@ import { CardConfig, CardType, SpecialCardList } from "../common/config/CardSet"
|
||||
import { CardUseComp } from "./CardUseComp";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { SkillSet } from "../common/config/SkillSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
|
||||
|
||||
|
||||
@@ -159,6 +161,19 @@ export class CardComp extends CCComp {
|
||||
|
||||
useCard(): CardConfig | null {
|
||||
if (!this.cardData || this.isUsing) return null;
|
||||
if (this.cardData.type === CardType.Hero) {
|
||||
const guard = {
|
||||
cancel: false,
|
||||
reason: "",
|
||||
uuid: this.cardData.uuid,
|
||||
hero_lv: this.cardData.hero_lv
|
||||
};
|
||||
oops.message.dispatchEvent(GameEvent.UseHeroCard, guard);
|
||||
if (guard.cancel) {
|
||||
this.playReboundAnim();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
this.isUsing = true;
|
||||
const used = this.cardData;
|
||||
mLogger.log(this.debugMode, "CardComp", "use card", {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CardConfig, CardInitCoins, CardsUpSet, getCardsByLv } from "../common/config/CardSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { CardComp } from "./CardComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
@@ -43,6 +42,8 @@ export class MissionCardComp extends CCComp {
|
||||
hero_info_node:Node = null! //场上英雄信息面板所在节点
|
||||
@property(Prefab)
|
||||
hero_info_prefab:Prefab=null! //场上英雄信息面板Prefab
|
||||
@property(Node)
|
||||
hero_num_node:Node=null!
|
||||
/** 预留图集缓存(后续接入按钮/卡面图标时复用) */
|
||||
private uiconsAtlas: SpriteAtlas | null = null;
|
||||
/** 四个槽位对应的单卡控制器缓存 */
|
||||
@@ -52,6 +53,11 @@ export class MissionCardComp extends CCComp {
|
||||
private coin: number = CardInitCoins;
|
||||
private readonly heroInfoItemGap: number = 86;
|
||||
private heroInfoSyncTimer: number = 0;
|
||||
private readonly heroDefaultMaxCount: number = 5;
|
||||
private readonly heroExtendMaxCount: number = 6;
|
||||
private heroMaxCount: number = this.heroDefaultMaxCount;
|
||||
private heroCurrentCount: number = 0;
|
||||
private heroNumLabel: Label | null = null;
|
||||
private heroInfoItems: Map<number, {
|
||||
node: Node,
|
||||
model: HeroAttrsComp,
|
||||
@@ -81,6 +87,8 @@ export class MissionCardComp extends CCComp {
|
||||
onMissionStart() {
|
||||
this.poolLv = CARD_POOL_INIT_LEVEL;
|
||||
this.coin = CardInitCoins
|
||||
this.heroMaxCount = this.heroDefaultMaxCount;
|
||||
this.heroCurrentCount = 0;
|
||||
this.clearHeroInfoPanels();
|
||||
this.layoutCardSlots();
|
||||
this.clearAllCards();
|
||||
@@ -91,6 +99,7 @@ export class MissionCardComp extends CCComp {
|
||||
this.resetButtonScale(this.cards_up);
|
||||
this.updatePoolLvUI();
|
||||
this.updateCoinUI();
|
||||
this.updateHeroNumUI(false, false);
|
||||
this.node.active = true;
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
@@ -129,6 +138,8 @@ export class MissionCardComp extends CCComp {
|
||||
this.on(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
this.on(GameEvent.CoinAdd, this.onCoinAdd, this);
|
||||
oops.message.on(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
oops.message.on(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.on(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
|
||||
/** 按钮事件:抽卡与卡池升级 */
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
@@ -148,6 +159,8 @@ export class MissionCardComp extends CCComp {
|
||||
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
||||
private unbindEvents() {
|
||||
oops.message.off(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.off(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||
@@ -161,7 +174,27 @@ export class MissionCardComp extends CCComp {
|
||||
const eid = Number(payload?.eid ?? 0);
|
||||
const model = payload?.model as HeroAttrsComp | undefined;
|
||||
if (!eid || !model) return;
|
||||
const before = this.heroCurrentCount;
|
||||
this.ensureHeroInfoPanel(eid, model);
|
||||
this.updateHeroNumUI(true, this.heroCurrentCount > before);
|
||||
}
|
||||
|
||||
private onHeroDead() {
|
||||
this.refreshHeroInfoPanels();
|
||||
this.updateHeroNumUI(true, false);
|
||||
}
|
||||
|
||||
private onUseHeroCard(event: string, args: any) {
|
||||
const payload = args ?? event;
|
||||
if (!payload) return;
|
||||
const current = this.getAliveHeroCount();
|
||||
this.heroCurrentCount = current;
|
||||
if (current >= this.heroMaxCount) {
|
||||
payload.cancel = true;
|
||||
payload.reason = "hero_limit";
|
||||
oops.gui.toast(`英雄已满 (${current}/${this.heroMaxCount})`);
|
||||
this.playHeroNumDeniedAnim();
|
||||
}
|
||||
}
|
||||
|
||||
private onDrawTouchStart() {
|
||||
@@ -402,6 +435,11 @@ export class MissionCardComp extends CCComp {
|
||||
removeKeys.push(eid);
|
||||
return;
|
||||
}
|
||||
if (item.model?.is_dead) {
|
||||
if (item.node.isValid) item.node.destroy();
|
||||
removeKeys.push(eid);
|
||||
return;
|
||||
}
|
||||
if (!item.comp.isModelAlive()) {
|
||||
if (item.node.isValid) item.node.destroy();
|
||||
removeKeys.push(eid);
|
||||
@@ -415,6 +453,7 @@ export class MissionCardComp extends CCComp {
|
||||
if (removeKeys.length > 0) {
|
||||
this.relayoutHeroInfoPanels();
|
||||
}
|
||||
this.updateHeroNumUI(false, false);
|
||||
}
|
||||
|
||||
private updateHeroInfoPanel(item: {
|
||||
@@ -449,6 +488,77 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
}
|
||||
this.heroInfoSyncTimer = 0;
|
||||
this.heroCurrentCount = 0;
|
||||
this.updateHeroNumUI(false, false);
|
||||
}
|
||||
|
||||
public setHeroMaxCount(max: number) {
|
||||
const next = Math.max(this.heroDefaultMaxCount, Math.min(this.heroExtendMaxCount, Math.floor(max || this.heroDefaultMaxCount)));
|
||||
if (next === this.heroMaxCount) return;
|
||||
this.heroMaxCount = next;
|
||||
this.updateHeroNumUI(true, false);
|
||||
}
|
||||
|
||||
public tryExpandHeroMax(add: number = 1): boolean {
|
||||
const next = this.heroMaxCount + Math.max(0, Math.floor(add));
|
||||
const before = this.heroMaxCount;
|
||||
this.setHeroMaxCount(next);
|
||||
return this.heroMaxCount > before;
|
||||
}
|
||||
|
||||
public canUseHeroCard(): boolean {
|
||||
return this.getAliveHeroCount() < this.heroMaxCount;
|
||||
}
|
||||
|
||||
private getAliveHeroCount(): number {
|
||||
let count = 0;
|
||||
this.heroInfoItems.forEach(item => {
|
||||
if (!item?.node || !item.node.isValid) return;
|
||||
if (!item.comp?.isModelAlive()) return;
|
||||
if (item.model?.is_dead) return;
|
||||
count += 1;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
private updateHeroNumUI(animate: boolean, isIncrease: boolean) {
|
||||
this.heroCurrentCount = this.getAliveHeroCount();
|
||||
if (!this.hero_num_node || !this.hero_num_node.isValid) return;
|
||||
const numNode = this.hero_num_node.getChildByName("num");
|
||||
if (!this.heroNumLabel) {
|
||||
this.heroNumLabel = numNode?.getComponent(Label) || numNode?.getComponentInChildren(Label) || null;
|
||||
}
|
||||
if (this.heroNumLabel) {
|
||||
this.heroNumLabel.string = `${this.heroCurrentCount}/${this.heroMaxCount}`;
|
||||
}
|
||||
if (!animate || !isIncrease) return;
|
||||
this.playHeroNumGainAnim();
|
||||
}
|
||||
|
||||
private playHeroNumGainAnim() {
|
||||
if (!this.hero_num_node || !this.hero_num_node.isValid) return;
|
||||
const iconNode = this.hero_num_node.getChildByName("icon");
|
||||
const numNode = this.hero_num_node.getChildByName("num");
|
||||
this.playHeroNumNodePop(iconNode, 1.16);
|
||||
this.playHeroNumNodePop(numNode, 1.16);
|
||||
}
|
||||
|
||||
private playHeroNumDeniedAnim() {
|
||||
if (!this.hero_num_node || !this.hero_num_node.isValid) return;
|
||||
const iconNode = this.hero_num_node.getChildByName("icon");
|
||||
const numNode = this.hero_num_node.getChildByName("num");
|
||||
this.playHeroNumNodePop(iconNode, 1.1);
|
||||
this.playHeroNumNodePop(numNode, 1.1);
|
||||
}
|
||||
|
||||
private playHeroNumNodePop(node: Node | null, scalePeak: number) {
|
||||
if (!node || !node.isValid) return;
|
||||
Tween.stopAllByTarget(node);
|
||||
node.setScale(1, 1, 1);
|
||||
tween(node)
|
||||
.to(0.08, { scale: new Vec3(scalePeak, scalePeak, 1) })
|
||||
.to(0.1, { scale: new Vec3(1, 1, 1) })
|
||||
.start();
|
||||
}
|
||||
|
||||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { _decorator, resources, Sprite, SpriteAtlas, SpriteFrame, v3, Vec3 } from "cc";
|
||||
import { _decorator, Node, resources, Sprite, SpriteAtlas, SpriteFrame, v3, Vec3 } 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 { Hero } from "../hero/Hero";
|
||||
@@ -41,7 +41,6 @@ export class MissionHeroCompComp extends CCComp {
|
||||
}
|
||||
fight_ready(){
|
||||
smc.vmdata.mission_data.hero_num=0
|
||||
|
||||
}
|
||||
// protected update(dt: number): void {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user