feat(关卡): 添加英雄数量上限机制
- 在 MissionCardComp 中添加英雄数量显示与上限控制逻辑 - 当英雄数量达到上限时禁止使用英雄卡牌 - 英雄死亡时减少当前英雄计数并刷新显示 - 添加英雄数量变化的动画反馈效果 - 移除 SingletonModuleComp 中未使用的 unlockCoin 字段
This commit is contained in:
@@ -10890,6 +10890,9 @@
|
|||||||
"__uuid__": "46f1e2cb-6fa7-4e9e-b419-e424ba47fe68",
|
"__uuid__": "46f1e2cb-6fa7-4e9e-b419-e424ba47fe68",
|
||||||
"__expectedType__": "cc.Prefab"
|
"__expectedType__": "cc.Prefab"
|
||||||
},
|
},
|
||||||
|
"hero_num_node": {
|
||||||
|
"__id__": 144
|
||||||
|
},
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
max_mission:4,//最大关卡
|
max_mission:4,//最大关卡
|
||||||
coin:0,
|
coin:0,
|
||||||
time:15*60,//游戏时间
|
time:15*60,//游戏时间
|
||||||
unlockCoin: 20,
|
|
||||||
},
|
},
|
||||||
scores: {
|
scores: {
|
||||||
score: 0, // 基础得分
|
score: 0, // 基础得分
|
||||||
|
|||||||
@@ -196,6 +196,16 @@ export class BattleEntityLifecycleSystem extends ecs.ComblockSystem
|
|||||||
const label = this.resolveLabel(heroAttrs);
|
const label = this.resolveLabel(heroAttrs);
|
||||||
if (heroAttrs) {
|
if (heroAttrs) {
|
||||||
mLogger.log(heroAttrs.debugMode, 'BattleEntityLifecycle', `${label}离开世界: ${heroAttrs.hero_name}`);
|
mLogger.log(heroAttrs.debugMode, 'BattleEntityLifecycle', `${label}离开世界: ${heroAttrs.hero_name}`);
|
||||||
|
if (heroAttrs.fac === FacSet.HERO) {
|
||||||
|
const missionData = smc.vmdata?.mission_data;
|
||||||
|
if (missionData) {
|
||||||
|
missionData.hero_num = Math.max(0, (missionData.hero_num ?? 0) - 1);
|
||||||
|
}
|
||||||
|
oops.message.dispatchEvent(GameEvent.HeroDead, {
|
||||||
|
eid: e.eid,
|
||||||
|
model: heroAttrs
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mLogger.log(true, 'BattleEntityLifecycle', `${label}离开世界: 实体ID ${e.eid}`);
|
mLogger.log(true, 'BattleEntityLifecycle', `${label}离开世界: 实体ID ${e.eid}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { CardConfig, CardType, SpecialCardList } from "../common/config/CardSet"
|
|||||||
import { CardUseComp } from "./CardUseComp";
|
import { CardUseComp } from "./CardUseComp";
|
||||||
import { HeroInfo } from "../common/config/heroSet";
|
import { HeroInfo } from "../common/config/heroSet";
|
||||||
import { SkillSet } from "../common/config/SkillSet";
|
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 {
|
useCard(): CardConfig | null {
|
||||||
if (!this.cardData || this.isUsing) return 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;
|
this.isUsing = true;
|
||||||
const used = this.cardData;
|
const used = this.cardData;
|
||||||
mLogger.log(this.debugMode, "CardComp", "use card", {
|
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 { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CardConfig, CardInitCoins, CardsUpSet, getCardsByLv } from "../common/config/CardSet";
|
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 { CardComp } from "./CardComp";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||||
@@ -43,6 +42,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
hero_info_node:Node = null! //场上英雄信息面板所在节点
|
hero_info_node:Node = null! //场上英雄信息面板所在节点
|
||||||
@property(Prefab)
|
@property(Prefab)
|
||||||
hero_info_prefab:Prefab=null! //场上英雄信息面板Prefab
|
hero_info_prefab:Prefab=null! //场上英雄信息面板Prefab
|
||||||
|
@property(Node)
|
||||||
|
hero_num_node:Node=null!
|
||||||
/** 预留图集缓存(后续接入按钮/卡面图标时复用) */
|
/** 预留图集缓存(后续接入按钮/卡面图标时复用) */
|
||||||
private uiconsAtlas: SpriteAtlas | null = null;
|
private uiconsAtlas: SpriteAtlas | null = null;
|
||||||
/** 四个槽位对应的单卡控制器缓存 */
|
/** 四个槽位对应的单卡控制器缓存 */
|
||||||
@@ -52,6 +53,11 @@ export class MissionCardComp extends CCComp {
|
|||||||
private coin: number = CardInitCoins;
|
private coin: number = CardInitCoins;
|
||||||
private readonly heroInfoItemGap: number = 86;
|
private readonly heroInfoItemGap: number = 86;
|
||||||
private heroInfoSyncTimer: number = 0;
|
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, {
|
private heroInfoItems: Map<number, {
|
||||||
node: Node,
|
node: Node,
|
||||||
model: HeroAttrsComp,
|
model: HeroAttrsComp,
|
||||||
@@ -81,6 +87,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
onMissionStart() {
|
onMissionStart() {
|
||||||
this.poolLv = CARD_POOL_INIT_LEVEL;
|
this.poolLv = CARD_POOL_INIT_LEVEL;
|
||||||
this.coin = CardInitCoins
|
this.coin = CardInitCoins
|
||||||
|
this.heroMaxCount = this.heroDefaultMaxCount;
|
||||||
|
this.heroCurrentCount = 0;
|
||||||
this.clearHeroInfoPanels();
|
this.clearHeroInfoPanels();
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
this.clearAllCards();
|
this.clearAllCards();
|
||||||
@@ -91,6 +99,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.resetButtonScale(this.cards_up);
|
this.resetButtonScale(this.cards_up);
|
||||||
this.updatePoolLvUI();
|
this.updatePoolLvUI();
|
||||||
this.updateCoinUI();
|
this.updateCoinUI();
|
||||||
|
this.updateHeroNumUI(false, false);
|
||||||
this.node.active = true;
|
this.node.active = true;
|
||||||
const cards = this.buildDrawCards();
|
const cards = this.buildDrawCards();
|
||||||
this.dispatchCardsToSlots(cards);
|
this.dispatchCardsToSlots(cards);
|
||||||
@@ -129,6 +138,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.on(GameEvent.MissionEnd, this.onMissionEnd, this);
|
this.on(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||||
this.on(GameEvent.CoinAdd, this.onCoinAdd, this);
|
this.on(GameEvent.CoinAdd, this.onCoinAdd, this);
|
||||||
oops.message.on(GameEvent.MasterCalled, this.onMasterCalled, 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);
|
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||||
@@ -148,6 +159,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
||||||
private unbindEvents() {
|
private unbindEvents() {
|
||||||
oops.message.off(GameEvent.MasterCalled, this.onMasterCalled, this);
|
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_START, this.onDrawTouchStart, this);
|
||||||
this.cards_chou?.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
this.cards_chou?.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||||
this.cards_chou?.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, 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 eid = Number(payload?.eid ?? 0);
|
||||||
const model = payload?.model as HeroAttrsComp | undefined;
|
const model = payload?.model as HeroAttrsComp | undefined;
|
||||||
if (!eid || !model) return;
|
if (!eid || !model) return;
|
||||||
|
const before = this.heroCurrentCount;
|
||||||
this.ensureHeroInfoPanel(eid, model);
|
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() {
|
private onDrawTouchStart() {
|
||||||
@@ -402,6 +435,11 @@ export class MissionCardComp extends CCComp {
|
|||||||
removeKeys.push(eid);
|
removeKeys.push(eid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (item.model?.is_dead) {
|
||||||
|
if (item.node.isValid) item.node.destroy();
|
||||||
|
removeKeys.push(eid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!item.comp.isModelAlive()) {
|
if (!item.comp.isModelAlive()) {
|
||||||
if (item.node.isValid) item.node.destroy();
|
if (item.node.isValid) item.node.destroy();
|
||||||
removeKeys.push(eid);
|
removeKeys.push(eid);
|
||||||
@@ -415,6 +453,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
if (removeKeys.length > 0) {
|
if (removeKeys.length > 0) {
|
||||||
this.relayoutHeroInfoPanels();
|
this.relayoutHeroInfoPanels();
|
||||||
}
|
}
|
||||||
|
this.updateHeroNumUI(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateHeroInfoPanel(item: {
|
private updateHeroInfoPanel(item: {
|
||||||
@@ -449,6 +488,77 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.heroInfoSyncTimer = 0;
|
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) 删除组件是触发组件处理自定义释放逻辑 */
|
/** 视图对象通过 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 { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { Hero } from "../hero/Hero";
|
import { Hero } from "../hero/Hero";
|
||||||
@@ -41,7 +41,6 @@ export class MissionHeroCompComp extends CCComp {
|
|||||||
}
|
}
|
||||||
fight_ready(){
|
fight_ready(){
|
||||||
smc.vmdata.mission_data.hero_num=0
|
smc.vmdata.mission_data.hero_num=0
|
||||||
|
|
||||||
}
|
}
|
||||||
// protected update(dt: number): void {
|
// protected update(dt: number): void {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user