diff --git a/assets/script/game/common/config/CardSet.ts b/assets/script/game/common/config/CardSet.ts index a8ce658f..2310729b 100644 --- a/assets/script/game/common/config/CardSet.ts +++ b/assets/script/game/common/config/CardSet.ts @@ -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 = { - 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 = { + 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 = { + 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) +} diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index def18e65..3102a793 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -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}`); diff --git a/assets/script/game/map/CardUseComp.ts b/assets/script/game/map/CardUseComp.ts index 4f23f7be..6f751042 100644 --- a/assets/script/game/map/CardUseComp.ts +++ b/assets/script/game/map/CardUseComp.ts @@ -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"; diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 43daa3e9..fd3a45e1 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -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;