feat: 拆分特殊卡类型并实现升级和刷新功能
- 将 CardType.Special 拆分为 SpecialUpgrade 和 SpecialRefresh - 新增特殊卡使用逻辑:升级功能卡可随机升级场上英雄,刷新功能卡可筛选卡池 - 添加 drawCardsByRule 函数支持按类型、英雄类型和等级抽取卡牌 - 在 MissionCardComp 中处理特殊卡使用事件并更新UI
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import * as exp from "constants"
|
||||
import { HeroInfo, HType } from "./heroSet"
|
||||
|
||||
/** 卡牌大类定义 */
|
||||
export enum CardType {
|
||||
Hero = 1,
|
||||
Skill = 2,
|
||||
Special = 3,
|
||||
SpecialUpgrade = 3,
|
||||
SpecialRefresh = 4,
|
||||
}
|
||||
|
||||
/** 卡池等级定义 */
|
||||
@@ -89,41 +91,58 @@ export const CardPoolList: CardConfig[] = [
|
||||
|
||||
{ uuid: 5304, type: CardType.Hero, cost: 3, weight: 25, lv: 6, hero_lv: 1 },
|
||||
|
||||
{ uuid: 7001, type: CardType.Special, cost: 1, weight: 20, lv: 1 },
|
||||
{ uuid: 7001, type: CardType.SpecialUpgrade, cost: 6, weight: 16, lv: 1 },
|
||||
{ uuid: 7002, type: CardType.SpecialUpgrade, cost: 6, weight: 14, lv: 2 },
|
||||
{ uuid: 7101, type: CardType.SpecialRefresh, cost: 4, weight: 14, lv: 1 },
|
||||
{ uuid: 7102, type: CardType.SpecialRefresh, cost: 4, weight: 14, lv: 1 },
|
||||
{ uuid: 7103, type: CardType.SpecialRefresh, cost: 5, weight: 12, lv: 2 },
|
||||
]
|
||||
|
||||
|
||||
/** 功能卡效果类型 */
|
||||
export enum SpecialEffectType {
|
||||
DrawHero = 1,
|
||||
RepeatNextUse = 2,
|
||||
export enum SpecialRefreshHeroType {
|
||||
Any = 0,
|
||||
Melee = 1,
|
||||
Ranged = 2,
|
||||
}
|
||||
|
||||
/** 功能卡效果参数 */
|
||||
export interface SpecialCardEffect {
|
||||
type: SpecialEffectType
|
||||
drawHeroCount?: number
|
||||
drawHeroLv?: CardKind
|
||||
repeatNextUseTimes?: number
|
||||
}
|
||||
|
||||
/** 功能卡完整配置 */
|
||||
export interface SpecialCardConfig extends CardConfig {
|
||||
/** 升级功能卡完整配置 */
|
||||
export interface SpecialUpgradeCardConfig extends CardConfig {
|
||||
name: string
|
||||
info: string
|
||||
effect: SpecialCardEffect
|
||||
currentLv: number
|
||||
targetLv: number
|
||||
}
|
||||
|
||||
/** 刷新功能卡完整配置 */
|
||||
export interface SpecialRefreshCardConfig extends CardConfig {
|
||||
name: string
|
||||
info: string
|
||||
refreshLv: number
|
||||
refreshHeroType: SpecialRefreshHeroType
|
||||
}
|
||||
|
||||
/** 功能卡定义表 */
|
||||
|
||||
|
||||
|
||||
export const SpecialCardList: Record<number, SpecialCardConfig> = {
|
||||
7001: { uuid: 7001,type: CardType.Special,cost: 6,weight: 20,lv: CardKind.LV1,name:"哈哈",info: "抽取4张等级3的英雄",
|
||||
effect: {type: SpecialEffectType.DrawHero,drawHeroCount: 4,drawHeroLv: CardKind.LV3,},
|
||||
export const SpecialUpgradeCardList: Record<number, SpecialUpgradeCardConfig> = {
|
||||
7001: { uuid: 7001,type: CardType.SpecialUpgrade,cost: 6,weight: 16,lv: CardKind.LV1,name:"战术晋升",info: "升级场上随机1个1级英雄到2级",
|
||||
currentLv: 1, targetLv: 2,
|
||||
},
|
||||
7002: { uuid: 7002,type: CardType.Special,cost: 5,weight: 20,lv: CardKind.LV2,name:"哈哈哈", info: "重复使用下一张卡1次",
|
||||
effect: {type: SpecialEffectType.RepeatNextUse,repeatNextUseTimes: 1,},
|
||||
7002: { uuid: 7002,type: CardType.SpecialUpgrade,cost: 6,weight: 14,lv: CardKind.LV2,name:"进阶战术",info: "升级场上随机1个2级英雄到3级",
|
||||
currentLv: 2, targetLv: 3,
|
||||
},
|
||||
}
|
||||
|
||||
export const SpecialRefreshCardList: Record<number, SpecialRefreshCardConfig> = {
|
||||
7101: { uuid: 7101,type: CardType.SpecialRefresh,cost: 4,weight: 14,lv: CardKind.LV1,name:"近战征召",info: "刷新卡池,都是近战英雄",
|
||||
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Melee,
|
||||
},
|
||||
7102: { uuid: 7102,type: CardType.SpecialRefresh,cost: 4,weight: 14,lv: CardKind.LV1,name:"远程征召",info: "刷新卡池,都是远程英雄",
|
||||
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Ranged,
|
||||
},
|
||||
7103: { uuid: 7103,type: CardType.SpecialRefresh,cost: 5,weight: 12,lv: CardKind.LV2,name:"精英筛选",info: "刷新卡池,都是3级卡池等级英雄",
|
||||
refreshLv: 3, refreshHeroType: SpecialRefreshHeroType.Any,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -193,3 +212,33 @@ export const getCardsByLv = (
|
||||
const others = pickCards(otherPool, 2)
|
||||
return [...heroes, ...others]
|
||||
}
|
||||
|
||||
export const drawCardsByRule = (
|
||||
lv: number,
|
||||
options: {
|
||||
count?: number
|
||||
onlyCurrentLv?: boolean
|
||||
type?: CardType | CardType[]
|
||||
heroType?: HType
|
||||
heroLv?: number
|
||||
} = {}
|
||||
): CardConfig[] => {
|
||||
const count = Math.max(0, Math.floor(options.count ?? 4))
|
||||
const onlyCurrentLv = options.onlyCurrentLv ?? false
|
||||
let pool = getCardPoolByLv(lv, onlyCurrentLv)
|
||||
if (options.type !== undefined) {
|
||||
const typeSet = normalizeTypeFilter(options.type)
|
||||
pool = pool.filter(card => typeSet.has(card.type))
|
||||
}
|
||||
if (options.heroType !== undefined || options.heroLv !== undefined) {
|
||||
pool = pool.filter(card => {
|
||||
if (card.type !== CardType.Hero) return false
|
||||
const hero = HeroInfo[card.uuid]
|
||||
if (!hero) return false
|
||||
if (options.heroType !== undefined && hero.type !== options.heroType) return false
|
||||
if (options.heroLv !== undefined && card.hero_lv !== options.heroLv) return false
|
||||
return true
|
||||
})
|
||||
}
|
||||
return pickCards(pool, count)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, resources } 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 { CardConfig, CardType, SpecialCardList } from "../common/config/CardSet";
|
||||
import { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList } from "../common/config/CardSet";
|
||||
import { CardUseComp } from "./CardUseComp";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { SkillSet } from "../common/config/SkillSet";
|
||||
@@ -367,10 +367,13 @@ export class CardComp extends CCComp {
|
||||
this.info_node.getChildByName("ap").getChildByName("val").getComponent(Label).string = `${HeroInfo[this.card_uuid].ap*this.cardData.hero_lv}`;
|
||||
this.info_node.getChildByName("hp").getChildByName("val").getComponent(Label).string = `${HeroInfo[this.card_uuid].hp*this.cardData.hero_lv}`;
|
||||
}else{
|
||||
this.setLabel(this.name_node, `${SpecialCardList[this.card_uuid].name}Lv.${this.cardData.lv}`);
|
||||
const specialCard = this.card_type === CardType.SpecialUpgrade
|
||||
? SpecialUpgradeCardList[this.card_uuid]
|
||||
: SpecialRefreshCardList[this.card_uuid];
|
||||
this.setLabel(this.name_node, `${specialCard?.name || ""}Lv.${this.cardData.lv}`);
|
||||
this.info_node.active = false;
|
||||
this.oinfo_node.active = true;
|
||||
this.oinfo_node.getChildByName("info").getComponent(Label).string = `${SpecialCardList[this.card_uuid].info}`;
|
||||
this.oinfo_node.getChildByName("info").getComponent(Label).string = `${specialCard?.info || ""}`;
|
||||
}
|
||||
|
||||
this.setLabel(this.cost_node, `${this.card_cost}`);
|
||||
|
||||
@@ -52,7 +52,9 @@ export class CardUseComp extends CCComp {
|
||||
return "hero";
|
||||
case CardType.Skill:
|
||||
return "skill";
|
||||
case CardType.Special:
|
||||
case CardType.SpecialUpgrade:
|
||||
case CardType.SpecialRefresh:
|
||||
oops.message.dispatchEvent(GameEvent.UseSpecialCard, used);
|
||||
return "special";
|
||||
default:
|
||||
return "unknown";
|
||||
|
||||
@@ -3,12 +3,15 @@ import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtla
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
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, CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE, CardConfig, CardsUpSet, getCardsByLv } from "../common/config/CardSet";
|
||||
import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE, CardConfig, CardType, CardsUpSet, drawCardsByRule, getCardsByLv, SpecialRefreshCardList, SpecialRefreshHeroType, SpecialUpgradeCardList } from "../common/config/CardSet";
|
||||
import { CardComp } from "./CardComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { HInfoComp } from "./HInfoComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroInfo, HType } from "../common/config/heroSet";
|
||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -152,6 +155,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.on(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
oops.message.on(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.on(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
oops.message.on(GameEvent.UseSpecialCard, this.onUseSpecialCard, this);
|
||||
|
||||
/** 按钮事件:抽卡与卡池升级 */
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
@@ -193,6 +197,7 @@ export class MissionCardComp extends CCComp {
|
||||
oops.message.off(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onHeroDead, this);
|
||||
oops.message.off(GameEvent.UseHeroCard, this.onUseHeroCard, this);
|
||||
oops.message.off(GameEvent.UseSpecialCard, this.onUseSpecialCard, 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);
|
||||
@@ -231,6 +236,30 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
private onUseSpecialCard(event: string, args: any) {
|
||||
const payload = args ?? event;
|
||||
const uuid = Number(payload?.uuid ?? 0);
|
||||
const type = Number(payload?.type ?? 0) as CardType;
|
||||
if (!uuid) return;
|
||||
let success = false;
|
||||
if (type === CardType.SpecialUpgrade) {
|
||||
const card = SpecialUpgradeCardList[uuid];
|
||||
if (!card) return;
|
||||
success = this.tryUpgradeOneHero(card.currentLv, card.targetLv);
|
||||
if (!success) oops.gui.toast(`场上没有可从${card.currentLv}级升到${card.targetLv}级的英雄`);
|
||||
} else if (type === CardType.SpecialRefresh) {
|
||||
const card = SpecialRefreshCardList[uuid];
|
||||
if (!card) return;
|
||||
success = this.tryRefreshHeroCardsByEffect(card.refreshHeroType, card.refreshLv);
|
||||
if (!success) oops.gui.toast("当前卡池无符合条件的英雄卡");
|
||||
}
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "use special card", {
|
||||
uuid,
|
||||
type,
|
||||
success
|
||||
});
|
||||
}
|
||||
|
||||
private onDrawTouchStart() {
|
||||
this.playButtonPressAnim(this.cards_chou);
|
||||
}
|
||||
@@ -365,6 +394,31 @@ export class MissionCardComp extends CCComp {
|
||||
return filled;
|
||||
}
|
||||
|
||||
private tryRefreshHeroCards(heroType?: HType, heroLv?: number): boolean {
|
||||
const cards = drawCardsByRule(this.poolLv, {
|
||||
count: 4,
|
||||
type: CardType.Hero,
|
||||
heroType,
|
||||
heroLv
|
||||
});
|
||||
if (cards.length <= 0) return false;
|
||||
this.layoutCardSlots();
|
||||
this.dispatchCardsToSlots(cards.slice(0, 4));
|
||||
return true;
|
||||
}
|
||||
|
||||
private tryRefreshHeroCardsByEffect(refreshHeroType: SpecialRefreshHeroType, refreshLv: number): boolean {
|
||||
const heroType = this.resolveRefreshHeroType(refreshHeroType);
|
||||
const heroLv = refreshLv > 0 ? refreshLv : undefined;
|
||||
return this.tryRefreshHeroCards(heroType, heroLv);
|
||||
}
|
||||
|
||||
private resolveRefreshHeroType(refreshHeroType: SpecialRefreshHeroType): HType | undefined {
|
||||
if (refreshHeroType === SpecialRefreshHeroType.Melee) return HType.Melee;
|
||||
if (refreshHeroType === SpecialRefreshHeroType.Ranged) return HType.Long;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** 全量分发给4槽;每个槽位是否接收由 CardComp 自己判断(锁定可跳过) */
|
||||
private dispatchCardsToSlots(cards: CardConfig[]) {
|
||||
for (let i = 0; i < this.cardComps.length; i++) {
|
||||
@@ -636,6 +690,56 @@ export class MissionCardComp extends CCComp {
|
||||
return count;
|
||||
}
|
||||
|
||||
private queryAliveHeroActors(): Array<{ model: HeroAttrsComp, view: HeroViewComp | null }> {
|
||||
const actors: Array<{ model: HeroAttrsComp, view: HeroViewComp | null }> = [];
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
if (!model) return;
|
||||
if (model.fac !== FacSet.HERO) return;
|
||||
if (model.is_dead) return;
|
||||
const view = entity.get(HeroViewComp);
|
||||
actors.push({ model, view });
|
||||
});
|
||||
return actors;
|
||||
}
|
||||
|
||||
private tryUpgradeOneHero(currentLv: number, targetLv: number): boolean {
|
||||
const fromLv = Math.max(1, Math.floor(currentLv));
|
||||
const toLv = Math.max(1, Math.floor(targetLv));
|
||||
if (toLv <= fromLv) return false;
|
||||
const candidates = this.queryAliveHeroActors().filter(item => item.model.lv === fromLv);
|
||||
if (candidates.length === 0) return false;
|
||||
const target = candidates[Math.floor(Math.random() * candidates.length)];
|
||||
this.applyHeroLevel(target.model, toLv);
|
||||
if (target.view) {
|
||||
target.view.palayBuff("buff_lvup");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private applyHeroLevel(model: HeroAttrsComp, targetLv: number) {
|
||||
const hero = HeroInfo[model.hero_uuid];
|
||||
if (!hero) return;
|
||||
const nextLv = Math.max(1, Math.min(3, Math.floor(targetLv)));
|
||||
const hpRate = model.hp_max > 0 ? model.hp / model.hp_max : 1;
|
||||
model.lv = nextLv;
|
||||
model.ap = hero.ap * nextLv;
|
||||
model.hp_max = hero.hp * nextLv;
|
||||
model.hp = Math.max(1, Math.floor(model.hp_max * Math.max(0, Math.min(1, hpRate))));
|
||||
model.skills = {};
|
||||
for (const key in hero.skills) {
|
||||
const skill = hero.skills[key];
|
||||
if (!skill) continue;
|
||||
model.skills[skill.uuid] = { ...skill, lv: Math.max(0, skill.lv + nextLv - 2), ccd: 0 };
|
||||
}
|
||||
model.updateSkillDistanceCache();
|
||||
model.dirty_hp = true;
|
||||
oops.message.dispatchEvent(GameEvent.HeroLvUp, {
|
||||
uuid: model.hero_uuid,
|
||||
lv: nextLv
|
||||
});
|
||||
}
|
||||
|
||||
private updateHeroNumUI(animate: boolean, isIncrease: boolean) {
|
||||
this.syncMissionHeroData();
|
||||
if (!animate || !isIncrease) return;
|
||||
|
||||
Reference in New Issue
Block a user